6 min read

Git : Filter-repo

We used AI while writing this content.

git filter-repo est un outil polyvalent et performant pour réécrire l'historique Git. Il remplace git filter-branch en offrant de meilleures performances (plusieurs ordres de grandeur plus rapide), plus de fonctionnalités, et une conception plus sûre. Le projet Git lui-même recommande maintenant d'utiliser git filter-repo plutôt que git filter-branch.


Prérequis

Avant d'utiliser git filter-repo, assurez-vous d'avoir :

  • Git >= 2.36.0
  • Python >= 3.6

Installation

1. Installation simple

Le moyen le plus simple est de placer le script dans votre $PATH :

# Télécharger le script
curl -o ~/.local/bin/git-filter-repo https://raw.githubusercontent.com/newren/git-filter-repo/main/git-filter-repo

# Rendre exécutable
chmod +x ~/.local/bin/git-filter-repo

2. Installation via gestionnaire de paquets

# Via pip (Python)
pip install git-filter-repo

# Ubuntu/Debian
sudo apt install git-filter-repo

# macOS (Homebrew)
brew install git-filter-repo

# Fedora
sudo dnf install git-filter-repo

3. Vérification de l'installation

git filter-repo --version

Cas d'usage courants

Supprimer un fichier de tout l'historique

git filter-repo --path fichier-sensible.txt --invert-paths

L'option --invert-paths inverse la sélection : au lieu de garder le fichier, on le supprime.


Supprimer un dossier entier

git filter-repo --path dossier-a-supprimer/ --invert-paths

Conserver uniquement certains chemins

# Garder uniquement le dossier src/
git filter-repo --path src/

# Garder plusieurs dossiers
git filter-repo --path src/ --path docs/ --path README.md

Déplacer tous les fichiers dans un sous-répertoire

git filter-repo --to-subdirectory-filter mon-projet/

Avant :

repo/
├── README.md
├── src/
└── docs/

Après :

repo/
└── mon-projet/
    ├── README.md
    ├── src/
    └── docs/

Cette opération réécrit tout l'historique comme si les fichiers avaient toujours été dans ce sous-répertoire.


Renommer des fichiers ou dossiers

# Renommer un fichier
git filter-repo --path-rename ancien-nom.txt:nouveau-nom.txt

# Renommer un dossier
git filter-repo --path-rename old-dir/:new-dir/

# Renommer plusieurs éléments
git filter-repo \
  --path-rename src/:lib/ \
  --path-rename README.txt:README.md

Modifier les préfixes de tags

# Ajouter un préfixe aux tags
git filter-repo --tag-rename '':'v'

# Remplacer un préfixe
git filter-repo --tag-rename 'old-':'new-'

Options avancées

Analyse préliminaire

Avant de modifier votre dépôt, analysez-le pour identifier ce qui prend de la place :

git filter-repo --analyze

Cela créé un dossier .git/filter-repo/analysis/ avec des rapports détaillés sur :

  • Les plus gros fichiers
  • Les extensions de fichiers
  • Les chemins les plus fréquents
  • L'historique des noms de fichiers

Combiner plusieurs opérations

git filter-repo \
  --path src/ \
  --path-rename src/:lib/ \
  --to-subdirectory-filter mon-module/ \
  --tag-rename '':'mon-module-'

Cette commande :

  1. Ne garde que le dossier src/
  2. Le renomme en lib/
  3. Place tout dans mon-module/
  4. Ajoute mon-module- aux tags

Utilisation avec des callbacks Python

Pour des modifications complexes, utilisez des callbacks :

git filter-repo --blob-callback '
  if filename == b"config.py":
    return blob.data.replace(b"SECRET_KEY", b"REDACTED")
'

Comparaison des approches

Outil Vitesse Facilité d'usage Sécurité Flexibilité
git filter-repo ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
git filter-branch ⭐⭐ ⭐⭐ ⭐⭐⭐
BFG Repo Cleaner ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐

Avantages de git filter-repo

Performance exceptionnelle : Plusieurs ordres de grandeur plus rapide que filter-branch
Sécurité renforcée : Détecte automatiquement si vous travaillez dans un clone frais
Nettoyage automatique : Supprime automatiquement les anciennes données et repacks le dépôt
Fonctionnalités riches : Support de renommage, filtrage, analyse, callbacks Python
Évite les pièges : Ne mélange pas ancien et nouvel historique
Recommandé officiellement : Par le projet Git lui-même


Points d'attention critiques

⚠️ Avertissements importants

  1. Clone frais obligatoire : Travaillez toujours dans un clone frais du dépôt

    git clone https://github.com/user/repo.git repo-filtered
    cd repo-filtered
    git filter-repo [options]
    
  2. L'historique est complètement réécrit : Tous les hashes de commit changent

  3. Coordination d'équipe nécessaire : Tous les collaborateurs devront cloner à nouveau

  4. Force push requis :

    git remote add origin https://github.com/user/repo.git
    git push origin --force --all
    git push origin --force --tags
    
  5. Sauvegardez avant : Créez une copie de secours

    git clone --mirror https://github.com/user/repo.git repo-backup.git
    

Workflow complet recommandé

Exemple : Extraire un sous-projet pour le fusionner ailleurs

# 1. Analyser le dépôt source
git clone https://github.com/org/monorepo.git projet-extrait
cd projet-extrait
git filter-repo --analyze

# 2. Examiner les rapports dans .git/filter-repo/analysis/

# 3. Extraire et réorganiser
git filter-repo \
  --path src/module/ \
  --path-rename src/module/:src/ \
  --to-subdirectory-filter projet-module/ \
  --tag-rename '':'module-'

# 4. Vérifier le résultat
git log --oneline --graph --all
ls -la

# 5. Ajouter le remote et pousser
git remote add origin https://github.com/org/nouveau-repo.git
git push origin --force --all
git push origin --force --tags

Résolution des problèmes courants

Erreur : "not running in a fresh clone"

Cause : git filter-repo refuse de s'exécuter dans un dépôt qui n'est pas un clone frais.

Solution :

# Option 1 : Utiliser un nouveau clone
git clone https://github.com/user/repo.git repo-fresh
cd repo-fresh

# Option 2 : Forcer (non recommandé)
git filter-repo --force [options]

Les remotes ont disparu

Cause : git filter-repo supprime automatiquement les remotes pour éviter les pushs accidentels.

Solution :

# Rajouter le remote après le filtrage
git remote add origin https://github.com/user/repo.git

Commits vides créés

Cause : Certains commits ne contiennent plus de modifications après filtrage.

Solution : git filter-repo supprime automatiquement ces commits (c'est voulu). Pour les garder :

git filter-repo --prune-empty never [autres options]

Exemples pratiques détaillés

1. Supprimer un mot de passe committé par erreur

# Clone frais
git clone https://github.com/user/repo.git repo-clean
cd repo-clean

# Supprimer le fichier
git filter-repo --path config/passwords.yml --invert-paths

# Vérifier
git log --all --oneline -- config/passwords.yml
# (ne devrait rien retourner)

# Pousser
git remote add origin https://github.com/user/repo.git
git push origin --force --all

2. Diviser un monorepo en plusieurs dépôts

# Pour chaque sous-projet
for module in frontend backend api; do
  git clone monorepo.git ${module}-repo
  cd ${module}-repo
  
  git filter-repo --path ${module}/ --path-rename ${module}/:
  
  git remote add origin https://github.com/org/${module}.git
  git push origin --force --all
  git push origin --force --tags
  
  cd ..
done

3. Fusionner plusieurs dépôts dans un monorepo

# Préparer chaque dépôt
for repo in proj-a proj-b proj-c; do
  git clone https://github.com/org/${repo}.git ${repo}-temp
  cd ${repo}-temp
  git filter-repo --to-subdirectory-filter ${repo}/
  cd ..
done

# Créer le monorepo
git init monorepo
cd monorepo

# Fusionner chaque dépôt
for repo in proj-a proj-b proj-c; do
  git remote add ${repo} ../${repo}-temp
  git fetch ${repo}
  git merge --allow-unrelated-histories ${repo}/main
  git remote remove ${repo}
done

Utilisation comme bibliothèque Python

Pour des besoins très spécifiques, utilisez git-filter-repo comme module Python :

#!/usr/bin/env python3
import git_filter_repo as fr

def my_blob_callback(blob, callback_metadata):
    # Remplacer du texte dans tous les fichiers
    if b'OLD_TEXT' in blob.data:
        blob.data = blob.data.replace(b'OLD_TEXT', b'NEW_TEXT')

args = fr.FilteringOptions.parse_args(['--force'])
filter = fr.RepoFilter(args, blob_callback=my_blob_callback)
filter.run()

Liens utiles


Résumé des options essentielles

Option Usage Exemple
--path Garder/filtrer des chemins --path src/
--invert-paths Inverser la sélection --path temp/ --invert-paths
--path-rename Renommer fichiers/dossiers --path-rename old/:new/
--to-subdirectory-filter Déplacer tout dans un sous-dossier --to-subdirectory-filter projet/
--tag-rename Renommer les tags --tag-rename '':'v'
--analyze Analyser le dépôt --analyze
--force Forcer sur dépôt non-frais --force (déconseillé)
--dry-run Simuler sans modifier --dry-run

Copyleft Statement

Renoncé du droit d'auteur

Much of our content is freely available under the Creative Commons BY-NC-ND 4.0 licence, which allows free distribution and republishing of our content for non-commercial purposes, as long as Ronzz.org is appropriately credited and the content is not being modified materially to express a different meaning than it is originally intended for. It must be noted that some images on Ronzz.org are the intellectual property of third parties. Our permission to use those images may not cover your reproduction. This does not affect your statutory rights.

Nous mettons la plupart de nos contenus disponibles gratuitement sous la licence Creative Commons By-NC-ND 4.0, qui permet une distribution et une republication gratuites de notre contenu à des fins non commerciales, tant que Ronzz.org est correctement crédité et que le contenu n'est pas modifié matériellement pour exprimer un sens différent que prévu à l'origine.Il faut noter que certaines images sur Ronzz.org sont des propriétés intellectuelles de tiers. Notre autorisation d'utiliser ces images peut ne pas couvrir votre reproduction. Cela n'affecte pas vos droits statutaires.