Git

GIT : SUPPRIMER UN FICHIER DE L'HISTORIQUE

D

Dimanche 5 mai 2019

Mis à jour le dimanche 5 mai 2019

Git : supprimer un fichier de l'historique

Par le passé, vous avez ajouté dans votre dépôt Git de gros fichiers que vous avez supprimés ensuite. Hors, la taille de votre dépôt Git reste conséquent à cause de ces gros fichiers qui sont apparus au moins une fois dans votre historique Git. Bonne nouvelle, nous pouvons les supprimer purement et simplement pour les retirer de l'historique et diminuer la taille de votre dépôt Git !

Vous avez ajouté des fichiers contenant des mots de passes ou des données sensibles que vous souhaitez faire disparaitre de votre dépôt Git ? Bonne nouvelle, nous pouvons supprimer ces fichiers purement et simplement !

Sauvegardez votre dépôt avant toute manipulation

Une mauvaise manipulation peut toujours arriver. Je vous conseille de sauvegarder votre dépôt avant toutes manipulations :
tar -zcvf monDepotGit.tar.gz monProjet/

Lister les fichiers les plus volumineux de votre dépôt Git

Vous pouvez exécuter cette commande pour obtenir la liste des fichiers les plus volumineux de votre historique Git. Utile pour repérer quels fichiers prennent le plus de place dans votre historique et pour diminuer la taille de votre dépôt Git facilement et rapidement :
git rev-list --objects --all | git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | sed -n 's/^blob //p' | sort --numeric-sort --key=2 | cut -c 1-12,41- | $(command -v gnumfmt || echo numfmt) --field=2 --to=iec-i --suffix=B --padding=7 --round=nearest
Voici ce que nous donne cette commande :
c/Mes documents/MonProjet
$ git rev-list --objects --all | git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | sed -n 's/^blob //p' | sort --numeric-sort --key=2 | cut -c 1-12,41- | $(command -v gnumfmt || echo numfmt) --field=2 --to=iec-i --suffix=B --padding=7 --round=nearest
e69de29bb2d1      0B var/cache/.gitkeep
2ec067f4c707     65B app/config/routing.yml
fd781ca99a0e     73B README.md
639ec2cd7e82    101B app/AppCache.php
05123b6782ff    113B src/AppBundle/AppBundle.php
4665fcae34a9    116B web/robots.txt
ada7e896548b    135B .htaccess
fb1de45bdb33    143B app/.htaccess
9e4815e604ee    209B src/AppBundle/Resources/views/templates/form/form_label.html.twig
02af762af528    210B src/AppBundle/Resources/views/templates/form/form_label.html.twig
394acfa93e93    233B src/AppBundle/Repository/PhotoRepository.php
6c198ea3cc55    234B src/AppBundle/Repository/PhotoRepository.php
470d7e198277    235B src/AppBundle/Repository/MembreRepository.php
47d46a2a15a4    236B src/AppBundle/Repository/MembreRepository.php
9ae1874a1911    239B src/AppBundle/Repository/CommandeRepository.php
be400ab8fa86    239B src/AppBundle/Repository/VetementRepository.php
e4ceeb628695    239B src/AppBundle/Repository/BlogPostRepository.php
10408065ee96    240B src/AppBundle/Repository/BlogPostRepository.php
5badf55d9d3f    240B src/AppBundle/Repository/CommandeRepository.php
ccbc627d9a03    240B src/AppBundle/Repository/VetementRepository.php
324260516d62    260B web/old1/plugins/rs/assets/white50.png
2f75ca1ce9d9    265B .gitignore
d20e6b7cadba    269B src/AppBundle/Resources/views/emails/user_registration.txt.twig
2f6d92503ffe    270B app/config/config_test.yml
[...]
f7d1a8bca226  2,6MiB web/photos/photo_2.jpg
7c31f7f00830  2,9MiB web/photos/photo_3.jpg
522f184cf9aa  3,7MiB web/photos/photo_1.jpg
0231ea1ac1f5  3,8MiB web/photos/photo_5.jpg
585901004a81  4,4MiB web/photos/photo_4.jpg
Les derniers éléments de la liste, comme indiqué, sont les fichiers les plus volumineux. Les fichiers web/photos/* sont des photos que j'ai un jour utilisé dans mon projet, que je n'utilise plus, que j'ai supprimées, mais qui sont encore présentes dans l'historique Git.
Je n'aurais jamais du ajouter ces photos à mon historique, ne débattons pas sur le pourquoi, voyons comment les supprimer purement et simplement pour faire comme si elles n'avaient jamais existées dans l'historique Git.

Outils facilitant la tâches

Sur internet, vous pourrez trouver des outils plus ou moins bien permettant de supprimer des fichiers de notre historique Git. Chacun ont des méthodes différentes pour le faire.
Il y a BFG Repo Cleaner qui permet de faire pas mal de chose assez facilement. Il nécessite d'avoir Java d'installé sur son ordinateur.
Par contre, il ne permet pas de supprimer un fichier précis facilement.
Pour supprimer un fichier précis de l'historique, vous devez retrouver l'identifiant unique de ce fichier
Vous pouvez retrouver l'identifiant d'un fichier grâce à cette commande :
git log --all --pretty=format:%H -- web/photos/photo_4.jpg | xargs -n1 -I% sh -c "git ls-tree % -- web/photos/photo_4.jpg"
En remplaçant bien entendu web/photos/photo_4.jpg par le fichier que vous recherchez dans votre dépôt.
Si trop de commits apparaissent, vous pouvez filtrer les résultats d'un commit en particulier en utilisant :
git log --all --pretty=format:%H -- web/photos/photo_4.jpg | xargs -n1 -I% sh -c "git ls-tree % -- web/photos/photo_4.jpg | grep -q 9d84ffg6 && echo %"
9d84ffg6 est le début de l'identifiant de votre commit.

Enfin, si vous souhaitez afficher tous les objets présents dans votre historique Git, vous pouvez faire ceci (attention, la liste risque d'être longue) :
git rev-list --all --objects


Utiliser git filter-branch

La méthode la plus sûre, mais peut-être la plus longue a s'exécuter, surtout si votre historique Git est conséquent, reste d'utiliser les commandes Git dédiées à cet usage.
(Pour voir la différence de taille avant et après la manipulation, vous pouvez noter la taille de votre dossier .git avant d'exécuter les prochaines commandes)
Pour supprimer un fichier précis de votre historique, de tous les commits, de toutes les branches, vous pouvez utilisez :
git filter-branch --tree-filter 'rm -f web/photos/photo_4.jpg' HEAD --all
Pour le supprimer seulement sur la branche sur laquelle vous êtes, vous pouvez utilisez :
git filter-branch --tree-filter 'rm -f web/photos/photo_4.jpg' HEAD
Par contre, gardez bien à l'esprit que si l'élément que vous avez supprimé est encore présent dans une autre branche, alors la taille de votre dépôt git ne diminuera pas.

Vous pouvez supprimer plusieurs fichiers à la fois en séparant les chemins par un espace, comme ceci :
git filter-branch --tree-filter 'rm -f web/photos/photo_4.jpg web/photos/photo_5.jpg web/photos/...' HEAD --all
Si vos chemins comportent des espaces, il suffit de les encadrer par des guillements doubles :
git filter-branch -f --tree-filter 'rm -f "web/photos de vacances/moi.jpg" "web/photos de vacances/nous.jpg" ' HEAD --all

Git prend beaucoup de précautions dans ses actes. Une fois cette commande exécutée, si vous regardez votre historique, vous vous retrouvez avec des branches ressemblant à refs/original/refs/heads/master. Elles correspondent à un backup de vos branches avant la manipulation.
Vu que ce backup est toujours présent, vous comprendrez que la taille de votre historique Git n'a pas encore diminué.
Il y a plusieurs méthodes pour supprimer ces branches de backup. Je vous propose de refaire un git filter-branch qui n'effectue aucune action pour supprimer cet ancien backup et en créé un nouveau qui est, en réalité, un copier/coller de votre branche actuelle sans modification :
git filter-branch -f --tree-filter ' ' HEAD --all
Vos anciens fichiers sont maintenant définitivement supprimés.

Vous pouvez réitérer l'opération autant de fois que vous le souhaitez, comme ceci :
git filter-branch -f --tree-filter 'rm -f "web/photos de vacances/moi.jpg" "web/photos de vacances/nous.jpg" ' HEAD --all
git filter-branch -f --tree-filter 'rm -f "web/photos de vacances/les autres.jpg" "web/photos de vacances/les voisins.jpg" ' HEAD --all
git filter-branch -f --tree-filter 'rm -f "web/photos de vacances/la plage.jpg" "web/photos de vacances/la montagne.jpg" ' HEAD --all
git filter-branch -f --tree-filter ' ' HEAD --all # Pour supprimer les anciens backup

Une fois que vous avez supprimé les fichiers inutiles que vous ne souhaitez pas garder dans votre historique, vous devez dire à Git de se débarrasser des objets qui ne sont plus liés à aucun commit de votre historique Git, comme ceci :
git reflog expire --expire-unreachable=all --all
git gc --aggressive --prune=now

(Vous pouvez maintenant retourner voir la taille de votre dossier .git, celle-ci doit avoir diminué en fonction de la taille des éléments que vous avez supprimés)

La dernière chose à faire est de pousser toutes vos modifications sur votre dépôt distant :
git push -f --all
-f sert à forcer le git push malgré la réécriture de l'historique Git et --all sert à pousser toutes vos branches.

Vous pouvez avoir plus d'informations sur la commande git filter-branch sur sa documentation officielle.


Une erreur ? une question ? une critique ? une faute ? un conseil ? ou tout simplement un merci ?

Lâche ton commentaire