410 votes

Supprimer le dossier et son contenu de l'historique de git/GitHub

Je travaillais sur un dépôt sur mon compte GitHub et c'est un problème sur lequel je suis tombé.

  • Projet Node.js avec un dossier avec quelques paquets npm installés
  • Les colis étaient en node_modules dossier
  • J'ai ajouté ce dossier au dépôt git et j'ai poussé le code sur github (je ne pensais pas à la partie npm à ce moment-là).
  • J'ai réalisé que vous n'avez pas vraiment besoin de ce dossier pour faire partie du code.
  • J'ai supprimé ce dossier, je l'ai poussé

À ce moment-là, la taille de l'ensemble du dépôt git était d'environ 6MB où le code réel (tout sauf ce dossier) n'était qu'à peu près 300 KB .

Ce que je cherche en fin de compte, c'est un moyen de se débarrasser des détails de ce dossier de paquets dans l'historique de git, de sorte que si quelqu'un le clone, il n'ait pas à télécharger 6 Mo d'historique alors que les seuls fichiers réels qu'il obtiendra à partir du dernier commit seront de 300 Ko.

J'ai cherché des solutions possibles pour cela et j'ai essayé les 2 méthodes suivantes

Le Gist semblait fonctionner où après avoir exécuté le script, il a montré qu'il s'est débarrassé de ce dossier et après cela, il a montré que 50 commits différents ont été modifiés. Mais il ne m'a pas laissé pousser ce code. Lorsque j'ai essayé de le pousser, il a dit Branch up to date mais a montré que 50 commits ont été modifiés lors d'une git status . Les deux autres méthodes n'ont pas aidé non plus.

Maintenant, même s'il a montré qu'il s'est débarrassé de l'historique de ce dossier, lorsque j'ai vérifié la taille de ce repo sur mon hôte local, il était toujours autour de 6 Mo. (J'ai également supprimé le refs/original mais n'a pas vu le changement dans la taille du repo).

Ce que je cherche à clarifier, c'est s'il y a un moyen de se débarrasser non seulement de l'historique des commits (qui est la seule chose que je pense qui s'est produite) mais aussi de ces fichiers que git conserve en supposant que l'on veuille faire un retour en arrière.

Supposons qu'une solution soit présentée et appliquée sur mon hôte local mais qu'elle ne puisse pas être reproduite sur ce dépôt GitHub, est-il possible de cloner ce dépôt, de revenir au premier commit et de le pousser (ou cela signifie-t-il que git aura toujours un historique de tous ces commits ? - aka. 6MB).

Mon objectif final ici est de trouver la meilleure façon de se débarrasser du contenu du dossier dans git afin qu'un utilisateur n'ait pas à télécharger 6MB de matériel et qu'il puisse toujours avoir les autres commits qui n'ont jamais touché le dossier des modules (c'est à peu près tout) dans l'historique de git.

Comment puis-je le faire ?

4 votes

Si l'une des réponses ci-dessous a résolu votre problème, vous devriez peut-être envisager d'en accepter une comme réponse à votre question. meta.stackexchange.com/questions/5234/

0 votes

La meilleure réponse est : stackoverflow.com/a/32886427/5973334

644voto

Mohsen Points 16856

Si vous êtes ici pour copier-coller du code :

Il s'agit d'un exemple qui supprime node_modules de l'histoire

git filter-branch --tree-filter "rm -rf node_modules" --prune-empty HEAD
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
echo node_modules/ >> .gitignore
git add .gitignore
git commit -m 'Removing node_modules from git history'
git gc
git push origin master --force

Ce que fait réellement git :

La première ligne itère à travers toutes les références sur le même arbre ( --tree-filter ) comme HEAD (votre branche actuelle), en lançant la commande rm -rf node_modules . Cette commande supprime le dossier node_modules ( -r sans -r , rm ne supprimera pas les dossiers), sans qu'aucune invite ne soit donnée à l'utilisateur ( -f ). L'ajout --prune-empty supprime les commits inutiles (qui ne changent rien) de manière récursive.

La deuxième ligne supprime la référence à cette ancienne branche.

Le reste des commandes est relativement simple.

4 votes

Juste une note en passant : j'ai utilisé git count-objects -v pour vérifier si les fichiers ont bien été supprimés mais la taille du dépôt reste la même jusqu'à ce que je clone à nouveau le dépôt. Git conserve une copie de tous les fichiers originaux, je pense.

1 votes

Et comment empêcher les autres de repousser ce répertoire ?

1 votes

@Petah ajoutant le dossier à .gitignore devrait le faire

295voto

Lee Netherton Points 4712

Je trouve que le --tree-filter utilisée dans d'autres réponses peut être très lente, en particulier sur les grands dépôts avec beaucoup de commits.

Voici la méthode que j'utilise pour supprimer complètement un répertoire de l'historique git en utilisant la fonction --index-filter qui s'exécute beaucoup plus rapidement :

# Make a fresh clone of YOUR_REPO
git clone YOUR_REPO
cd YOUR_REPO

# Create tracking branches of all branches
for remote in `git branch -r | grep -v /HEAD`; do git checkout --track $remote ; done

# Remove DIRECTORY_NAME from all commits, then remove the refs to the old commits
# (repeat these two commands for as many directories that you want to remove)
git filter-branch --index-filter 'git rm -rf --cached --ignore-unmatch DIRECTORY_NAME/' --prune-empty --tag-name-filter cat -- --all
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d

# Ensure all old refs are fully removed
rm -Rf .git/logs .git/refs/original

# Perform a garbage collection to remove commits with no refs
git gc --prune=all --aggressive

# Force push all branches to overwrite their history
# (use with caution!)
git push origin --all --force
git push origin --tags --force

Vous pouvez vérifier la taille du référentiel avant et après l'opération gc avec :

git count-objects -vH

4 votes

Pourriez-vous expliquer pourquoi c'est beaucoup plus rapide ?

7 votes

@knocte : à partir de la documentation ( git-scm.com/docs/git-filter-branch ). "--index-filter : ... est similaire au filtre de l'arbre mais ne vérifie pas l'arbre, ce qui le rend beaucoup plus rapide".

26 votes

Pourquoi n'est-ce pas la réponse acceptée ? C'est si complet.

82voto

André Anjos Points 699

Il semble que la réponse actuelle à cette question consiste à pas utiliser filter-branch directement (du moins git lui-même ne le recommande plus), et de reporter ce travail à un outil externe. En particulier, git-filtre-repo est actuellement recommandé. L'auteur de cet outil fournit des arguments sur la raison pour laquelle l'utilisation filter-branch directement peut mener à des problèmes.

La plupart des scripts multi-lignes ci-dessus pour enlever dir de l'histoire pourrait être réécrite comme suit :

git filter-repo --path dir --invert-paths

L'outil est plus puissant que cela, apparemment. Vous pouvez appliquer des filtres par auteur, courriel, nom de référence et plus encore ( Page de manuel complète ici ). En outre, il est rapide . L'installation est facile - il s'agit distribués dans une variété de formats .

9 votes

Bel outil ! Fonctionne bien sur Ubuntu 20.04, vous pouvez juste pip3 install git-filter-repo puisqu'il est stdlib-only et n'installe aucune dépendance. Sur Ubuntu 18, il est incompatible avec la version git de la distro. Error: need a version of git whose diff-tree command has the --combined-all-paths option mais il est assez facile de le faire fonctionner sur une docker run -ti ubuntu:20.04

0 votes

Ça marche, c'est simple et élégant ! Merci pour la recommandation !

1 votes

Vous avez raison ! Mais s'il vous plaît si vous pouvez séparer la réponse de l'information sur filter-repo .. Je veux dire, peut-être écrire toutes les informations sur les filter-repo remplacer filter-branch puis écrire un ------- puis donnez-nous plus d'informations sur la commande elle-même - quelle est la --invert-paths par exemple. Merci !

55voto

participant Points 1816

En plus de la réponse populaire au-dessus de Je voudrais ajouter quelques notes pour Windows -systèmes. La commande

git filter-branch --tree-filter 'rm -rf node_modules' --prune-empty HEAD
  • fonctionne parfaitement sans aucune modification ! Par conséquent, vous ne devez pas utiliser Remove-Item , del ou n'importe quoi d'autre au lieu de rm -rf .

  • Si vous devez spécifier un chemin d'accès à un fichier ou un répertoire, utilisez barres obliques comme ./path/to/node_modules

0 votes

Cela ne fonctionnera pas sous Windows si le répertoire contient un . (point) dans son nom.

5 votes

Et j'ai trouvé la solution. Utilisez des doubles guillemets pour la commande rm comme ceci : "rm -rf node.modules".

26voto

Kim T Points 434

La meilleure méthode et la plus précise que j'ai trouvée est de télécharger le fichier bfg.jar : https://rtyley.github.io/bfg-repo-cleaner/

Ensuite, exécutez les commandes :

git clone --bare https://project/repository project-repository
cd project-repository
java -jar bfg.jar --delete-folders DIRECTORY_NAME
git reflog expire --expire=now --all && git gc --prune=now --aggressive
git push --mirror https://project/new-repository

Si vous voulez supprimer des fichiers, utilisez plutôt l'option delete-files :

java -jar bfg.jar --delete-files *.pyc

1 votes

Très facile :) si vous voulez être sûr que seul un dossier spécifique est supprimé, ceci vous aidera : stackoverflow.com/questions/21142986/

0 votes

Mais l'utilisation de BFG peut avoir des difficultés lorsqu'il y a plusieurs dossiers qui ont le même nom que celui que vous voulez supprimer, c'est-à-dire que BFG ne peut pas accepter le nom du chemin pour --delete-folders .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X