Parfois, je laissais tomber un DVD dans un projet de site Web, puis négligemment git commit -a -m ...
, et, zap, le repo était surchargé de 2,2 concerts. La prochaine fois que j'ai apporté quelques modifications, j'ai supprimé le fichier vidéo et tout validé, mais le fichier compressé est toujours présent dans le référentiel, dans l'historique.
Je sais que je peux démarrer des branches à partir de ces commits et rebaser une branche sur une autre. Mais que dois-je faire pour fusionner les 2 commits afin que le gros fichier ne soit pas affiché dans l'historique et qu'il soit nettoyé lors de la procédure de récupération de place?
Utilisez le BFG Repo-Cleaner , une alternative plus simple et plus rapide à git-filter-branch
, spécialement conçue pour supprimer les fichiers non souhaités de l’historique Git.
Suivez attentivement les instructions d'utilisation , la partie principale est juste ceci:
$ Java -jar bfg.jar --strip-blobs-bigger-than 100M my-repo.git
Tous les fichiers de plus de 100 Mo (qui ne font pas partie de votre dernier commit ) seront supprimés de l'historique de votre référentiel Git. Vous pouvez ensuite utiliser git gc
pour effacer les données mortes:
$ git gc --Prune=now --aggressive
Le BFG est généralement au moins 10-50x plus rapide que l'exécution de git-filter-branch
, et est généralement plus facile à utiliser.
Divulgation complète: je suis l'auteur du BFG Repo-Cleaner.
Ce que vous voulez faire est très perturbant si vous avez publié l'historique à d'autres développeurs. Voir "Récupération à partir de la base de données amont" dans la documentation _git rebase
_ pour connaître les étapes nécessaires après la réparation de votre historique.
Vous avez au moins deux options: _git filter-branch
_ et une base interactive, expliqués ci-dessous.
git filter-branch
_J'ai eu un problème similaire avec des données de test binaires volumineuses provenant d'une importation Subversion et j'ai écrit à propos de suppression des données d'un référentiel git .
Dites que votre histoire de git est:
_$ git lola --name-status
* f772d66 (HEAD, master) Login page
| A login.html
* cb14efd Remove DVD-rip
| D oops.iso
* ce36c98 Careless
| A oops.iso
| A other.html
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
_
Notez que git lola
est un alias non standard mais très utile. Avec le commutateur _--name-status
_, nous pouvons voir les modifications de l’arborescence associées à chaque commit.
Dans le commit “Careless” (dont le nom d'objet SHA1 est ce36c98), le fichier _oops.iso
_ est le DVD-rip ajouté par accident et supprimé du prochain commit, cb14efd. En utilisant la technique décrite dans l'article de blog susmentionné, la commande à exécuter est la suivante:
_git filter-branch --Prune-empty -d /dev/shm/scratch \
--index-filter "git rm --cached -f --ignore-unmatch oops.iso" \
--tag-name-filter cat -- --all
_
Les options:
--Prune-empty
_ supprime les validations qui deviennent vides (, c'est-à-dire , ne changez pas l'arborescence) à la suite de l'opération de filtrage. Dans le cas typique, cette option produit un historique plus propre.-d
_ nomme un répertoire temporaire inexistant à utiliser pour créer l'historique filtré. Si vous utilisez une distribution Linux moderne, spécifier ne arborescence dans _/dev/shm
_ entraînera une exécution plus rapide .--index-filter
_ est l'événement principal et s'exécute sur l'index à chaque étape de l'historique. Vous voulez supprimer _oops.iso
_ partout où il se trouve, mais il n’est pas présent dans tous les commits. La commande _git rm --cached -f --ignore-unmatch oops.iso
_ supprime le DVD-rip quand il est présent et n'échoue pas autrement.--tag-name-filter
_ explique comment réécrire les noms de balises. Un filtre de cat
est l'opération d'identité. Votre référentiel, comme l'exemple ci-dessus, peut ne pas avoir de balises, mais j'ai inclus cette option pour une généralité complète.--
_ spécifie la fin des options à _git filter-branch
_--all
_ suivant _--
_ est un raccourci pour toutes les références. Votre référentiel, comme dans l'exemple ci-dessus, peut ne comporter qu'une seule référence (maître), mais j'ai inclus cette option pour une généralité complète.Après un peu de barattage, l'histoire est maintenant:
_$ git lola --name-status
* 8e0a11c (HEAD, master) Login page
| A login.html
* e45ac59 Careless
| A other.html
| * f772d66 (refs/original/refs/heads/master) Login page
| | A login.html
| * cb14efd Remove DVD-rip
| | D oops.iso
| * ce36c98 Careless
|/
| A oops.iso
| A other.html
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
_
Notez que le nouveau commit "Careless" n’ajoute que _other.html
_ et que le commit "Remove DVD-rip" n’apparaît plus dans la branche principale. La branche intitulée _refs/original/refs/heads/master
_ contient vos commits d'origine en cas d'erreur. Pour le supprimer, suivez les étapes décrites dans "Liste de contrôle pour la réduction d’un référentiel".
_$ git update-ref -d refs/original/refs/heads/master
$ git reflog expire --expire=now --all
$ git gc --Prune=now
_
Pour une alternative plus simple, clonez le référentiel pour supprimer les bits indésirables.
_$ cd ~/src
$ mv repo repo.old
$ git clone file:///home/user/src/repo.old repo
_
L'utilisation d'une URL de clonage _file:///...
_ permet de copier des objets plutôt que de créer des liens physiques.
Maintenant, votre histoire est:
_$ git lola --name-status
* 8e0a11c (HEAD, master) Login page
| A login.html
* e45ac59 Careless
| A other.html
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
_
Les noms d'objet SHA1 pour les deux premiers validations ("Index" et "Page administrateur") sont restés inchangés car l'opération de filtrage n'a pas modifié ces validations. “Careless” lost _oops.iso
_ et “Login page” ont un nouveau parent; leurs SHA1 ont donc changé .
Avec une histoire de:
_$ git lola --name-status
* f772d66 (HEAD, master) Login page
| A login.html
* cb14efd Remove DVD-rip
| D oops.iso
* ce36c98 Careless
| A oops.iso
| A other.html
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
_
vous voulez supprimer _oops.iso
_ de "Insouciant" comme si vous ne l'aviez jamais ajouté, puis "Supprimer le DVD-rip" ne vous servirait à rien. Par conséquent, notre plan pour créer une base interactive est de conserver la "page d'administration", d'éditer "sans attention" et de supprimer "Supprimer le rip de DVD".
L'exécution de _$ git rebase -i 5af4522
_ démarre un éditeur avec le contenu suivant.
_pick ce36c98 Careless
pick cb14efd Remove DVD-rip
pick f772d66 Login page
# Rebase 5af4522..f772d66 onto 5af4522
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using Shell
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#
_
En exécutant notre plan, nous le modifions pour
_edit ce36c98 Careless
pick f772d66 Login page
# Rebase 5af4522..f772d66 onto 5af4522
# ...
_
C'est-à-dire que nous supprimons la ligne avec "Remove DVD-rip" et modifions l'opération sur "Careless" en edit
plutôt que pick
.
Enregistrer-quitter l'éditeur nous dépose à une invite de commande avec le message suivant.
_Stopped at ce36c98... Careless
You can amend the commit now, with
git commit --amend
Once you are satisfied with your changes, run
git rebase --continue
_
Comme le message nous l'indique, nous sommes sur le commit "Careless" que nous souhaitons éditer. Nous exécutons donc deux commandes.
_$ git rm --cached oops.iso
$ git commit --amend -C HEAD
$ git rebase --continue
_
Le premier supprime le fichier incriminé de l'index. Le second modifie ou modifie "Careless" pour qu'il soit l'index mis à jour et _-C HEAD
_ indique à git de réutiliser l'ancien message de validation. Enfin, _git rebase --continue
_ continue avec le reste de l'opération de rebasement.
Cela donne une histoire de:
_$ git lola --name-status
* 93174be (HEAD, master) Login page
| A login.html
* a570198 Careless
| A other.html
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
_
c'est ce que tu veux.
Pourquoi ne pas utiliser cette commande simple mais puissante?
git filter-branch --tree-filter 'rm -f DVD-rip' HEAD
L'option --tree-filter
exécute la commande spécifiée après chaque extraction du projet, puis recommande à nouveau les résultats. Dans ce cas, vous supprimez un fichier appelé DVD-rip de chaque instantané, qu’il existe ou non.
Voir ce lien .
(La meilleure réponse que j'ai vue à ce problème est la suivante: https://stackoverflow.com/a/42544963/714112 , copié ici car ce fil de discussion apparaît haut dans les classements de recherche Google, mais cet autre ne 't)
Ce script shell affiche tous les objets blob du référentiel, classés du plus petit au plus grand.
Pour mon exemple de dépôt, il fonctionnait environ 100 fois plus vite que les autres trouvés ici.
Sur mon fidèle système Athlon II X4, il gère le référentiel du noyau Linux avec ses 5 622 155 objets en à peine plus d’une minute.
git rev-list --objects --all \
| git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
| awk '/^blob/ {print substr($0,6)}' \
| sort --numeric-sort --key=2 \
| cut --complement --characters=13-40 \
| numfmt --field=2 --to=iec-i --suffix=B --padding=7 --round=nearest
Lorsque vous exécutez le code ci-dessus, vous obtenez Nice une sortie lisible par l'homme comme ceci:
...
0d99bb931299 530KiB path/to/some-image.jpg
2ba44098e28f 12MiB path/to/hires-image.png
bd1741ddce0d 63MiB path/to/some-video-1080p.mp4
Supposons que vous vouliez ensuite supprimer les fichiers a
et b
de chaque commit accessible de HEAD
, vous pouvez utiliser cette commande:
git filter-branch --index-filter 'git rm --cached --ignore-unmatch a b' HEAD
Ces commandes ont fonctionné dans mon cas:
git filter-branch --force --index-filter 'git rm --cached -r --ignore-unmatch oops.iso' --Prune-empty --tag-name-filter cat -- --all
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --Prune=now
git gc --aggressive --Prune=now
C'est un peu différent des versions ci-dessus.
Pour ceux qui ont besoin de pousser ceci à github/bitbucket (je l’ai seulement testé avec bitbucket):
# WARNING!!!
# this will rewrite completely your bitbucket refs
# will delete all branches that you didn't have in your local
git Push --all --Prune --force
# Once you pushed, all your teammates need to clone repository again
# git pull will not work
Après avoir essayé pratiquement toutes les réponses dans SO, j’ai enfin trouvé ce petit bijou qui a rapidement supprimé et supprimé les gros fichiers de mon référentiel et m’a permis de synchroniser à nouveau: http://www.zyxware.com/articles/4027/how -to-supprimer-les-fichiers-en-permanence-de-votre-repertoire-local-et-distant-git
CD dans votre dossier de travail local et exécutez la commande suivante:
git filter-branch -f --index-filter "git rm -rf --cached --ignore-unmatch FOLDERNAME" -- --all
remplacez FOLDERNAME par le fichier ou le dossier que vous souhaitez supprimer du référentiel git donné.
Une fois que cela est fait, exécutez les commandes suivantes pour nettoyer le référentiel local:
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --Prune=now
git gc --aggressive --Prune=now
Maintenant, envoyez toutes les modifications au référentiel distant:
git Push --all --force
Cela nettoiera le référentiel distant.
Notez juste que ces commandes peuvent être très destructives. Si davantage de personnes travaillent sur le repo, elles devront toutes tirer le nouvel arbre. Les trois commandes du milieu ne sont pas nécessaires si votre objectif n'est PAS de réduire la taille. Parce que la branche de filtre crée une sauvegarde du fichier supprimé et qu'il peut y rester longtemps.
$ git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch YOURFILENAME" HEAD
$ rm -rf .git/refs/original/
$ git reflog expire --all
$ git gc --aggressive --Prune
$ git Push Origin master --force
git filter-branch --tree-filter 'rm -f path/to/file' HEAD
a plutôt bien fonctionné pour moi, même si j'ai rencontré le même problème que décrit ici , que j'ai résolu en suivant cette suggestion .
Le livre pro-git contient un chapitre entier sur historique de la réécriture - consultez la section filter-branch
/Suppression d’un fichier de chaque validation .
Si vous savez que votre commit était récent au lieu de parcourir l'intégralité de l'arborescence, procédez comme suit: git filter-branch --tree-filter 'rm LARGE_FILE.Zip' HEAD~10..HEAD
J'ai rencontré ce problème avec un compte bitbucket, où j'avais accidentellement stocké des sauvegardes ginormous * .jpa de mon site.
git filter-branch --Prune-empty --index-filter 'git rm -rf --cached --ignore-unmatch MY-BIG-DIRECTORY-OR-FILE' --tag-name-filter cat -- --all
Relacez MY-BIG-DIRECTORY
avec le dossier en question pour réécrire complètement votre historique (, y compris les balises ).
source: http://naleid.com/blog/2012/01/17/finding-and-purging-big-files-from-git-history
J'ai essentiellement fait ce qui était sur cette réponse: https://stackoverflow.com/a/11032521/128642
(pour l'histoire, je vais le copier-coller ici)
$ git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch YOURFILENAME" HEAD
$ rm -rf .git/refs/original/
$ git reflog expire --all
$ git gc --aggressive --Prune
$ git Push Origin master --force
Cela n'a pas fonctionné, car j'aime renommer et déplacer beaucoup de choses. Ainsi, certains gros fichiers se trouvaient dans des dossiers renommés, et je pense que le gc ne pourrait pas supprimer la référence à ces fichiers en raison de la référence dans les objets tree
pointant vers ces fichiers. Ma solution ultime pour vraiment le tuer était de:
# First, apply what's in the answer linked in the front
# and before doing the gc --Prune --aggressive, do:
# Go back at the Origin of the repository
git checkout -b newinit <sha1 of first commit>
# Create a parallel initial commit
git commit --amend
# go back on the master branch that has big file
# still referenced in history, even though
# we thought we removed them.
git checkout master
# rebase on the newinit created earlier. By reapply patches,
# it will really forget about the references to hidden big files.
git rebase newinit
# Do the previous part (checkout + rebase) for each branch
# still connected to the original initial commit,
# so we remove all the references.
# Remove the .git/logs folder, also containing references
# to commits that could make git gc not remove them.
rm -rf .git/logs/
# Then you can do a garbage collection,
# and the hidden files really will get gc'ed
git gc --Prune --aggressive
Mon repo (le .git
) est passé de 32 Mo à 388 Ko, même la branche de filtre ne peut pas nettoyer.
Vous pouvez le faire en utilisant la commande branch filter
:
git filter-branch --tree-filter 'rm -rf path/to/your/file' HEAD
git filter-branch
est une commande puissante que vous pouvez utiliser pour supprimer un fichier volumineux de l'historique des commits. Le fichier restera pendant un moment et Git le supprimera dans la prochaine collecte de place. Vous trouverez ci-dessous le processus complet à partir de suppression de fichiers de l'historique de validation . Pour des raisons de sécurité, il exécute d'abord la commande sur une nouvelle branche:
# Do it in a new testing branch
$ git checkout -b test
# Remove file-name from every commit on the new branch
# --index-filter, rewrite index without checking out
# --cached, remove it from index but not include working tree
# --ignore-unmatch, ignore if files to be removed are absent in a commit
# HEAD, execute the specified command for each commit reached from HEAD by parent link
$ git filter-branch --index-filter 'git rm --cached --ignore-unmatch file-name' HEAD
# The output is OK, reset it to the prior branch master
$ git checkout master
$ git reset --soft test
# Remove test branch
$ git branch -rm test
# Push it with force
$ git Push --force Origin master
Utilisez Git Extensions , c’est un outil d’interface utilisateur. Il possède un plugin nommé "Rechercher des fichiers volumineux" qui trouve les fichiers de stockage dans des référentiels et permet de les supprimer de manière permanente.
N'utilisez pas 'git filter-branch' avant d'utiliser cet outil, car il ne pourra pas trouver les fichiers supprimés par 'filter-branch' (Altough 'filter-branch' ne supprime pas complètement les fichiers des fichiers du pack de référentiel). .
Lorsque vous rencontrez ce problème, git rm
ne suffira pas, car git se souvient que le fichier existait une fois dans notre historique et en conservera donc une référence.
Pour aggraver les choses, rebaser n'est pas facile non plus, car toute référence au blob empêchera git garbage collector de nettoyer l'espace. Ceci inclut les références distantes et les références de reflog.
J'ai assemblé git forget-blob
, un petit script qui tente de supprimer toutes ces références, puis utilise git filter-branch pour réécrire chaque commit de la branche.
Une fois que votre blob n'est plus référencé, git gc
s'en débarrassera
L'utilisation est assez simple git forget-blob file-to-forget
. Vous pouvez obtenir plus d'informations ici
J'ai mis cela en place grâce aux réponses de Stack Overflow et à quelques entrées de blog. Crédits à eux!