Filter branch
Guide Complet : git filter-branch
Section intitulée « Guide Complet : git filter-branch »Vue d’ensemble
Section intitulée « Vue d’ensemble »git filter-branch est un outil Git qui permet de réécrire l’historique en appliquant des filtres à chaque commit. C’est l’équivalent d’une “machine à remonter le temps” qui rejoue l’historique avec des modifications.
Principe de fonctionnement
Section intitulée « Principe de fonctionnement »Historique original: A → B → C → D → E (HEAD) ↓Filtrage: A' → B' → C' → D' → E' (nouveau HEAD)Chaque commit est “rejoué” avec les modifications spécifiées par les filtres.
Types de filtres disponibles
Section intitulée « Types de filtres disponibles »1. —index-filter (Le plus utilisé)
Section intitulée « 1. —index-filter (Le plus utilisé) »Modifie l’index Git (staging area) à chaque commit.
git filter-branch --index-filter 'COMMANDE' [OPTIONS]Avantages :
- ✅ Très rapide (pas de checkout des fichiers)
- ✅ Idéal pour supprimer des fichiers
- ✅ Fonctionne même avec de gros dépôts
Exemple typique :
# Supprimer un fichier de tout l'historiquegit filter-branch --index-filter 'git rm --cached --ignore-unmatch secrets.txt'2. —tree-filter
Section intitulée « 2. —tree-filter »Modifie l’arbre de travail (working directory) à chaque commit.
git filter-branch --tree-filter 'COMMANDE' [OPTIONS]Avantages :
- ✅ Plus intuitif (travaille avec des fichiers réels)
- ✅ Permet des opérations complexes sur les fichiers
Inconvénients :
- ❌ Plus lent (checkout à chaque commit)
- ❌ Consomme plus d’espace disque
Exemple :
# Supprimer tous les fichiers .loggit filter-branch --tree-filter 'find . -name "*.log" -delete'3. —msg-filter
Section intitulée « 3. —msg-filter »Modifie les messages de commit.
# Remplacer un mot dans tous les messagesgit filter-branch --msg-filter 'sed "s/password/credential/g"'4. —env-filter
Section intitulée « 4. —env-filter »Modifie les variables d’environnement (auteur, date, etc.).
# Changer l'email de l'auteurgit filter-branch --env-filter 'if [ "$GIT_AUTHOR_EMAIL" = "old@example.com" ]; then export GIT_AUTHOR_EMAIL="new@example.com"fi'5. —commit-filter
Section intitulée « 5. —commit-filter »Le plus puissant : permet de réécrire complètement les commits.
# Fusionner des commits ou les supprimer complètementgit filter-branch --commit-filter 'git commit-tree "$@"'Syntaxe détaillée
Section intitulée « Syntaxe détaillée »Commande complète
Section intitulée « Commande complète »git filter-branch [OPTIONS] [--] [RÉVISIONS]Options importantes
Section intitulée « Options importantes »| Option | Description | Usage |
|---|---|---|
--force ou -f | Force l’exécution même si backup existe | Toujours recommandé |
--prune-empty | Supprime les commits vides après filtrage | Essentiel pour un historique propre |
--tag-name-filter | Filtre les noms de tags | cat pour conserver, 'sed ...' pour modifier |
--subdirectory-filter | Extrait un sous-répertoire | Créer un nouveau dépôt d’un dossier |
-- | Sépare les options des révisions | Évite les ambiguïtés |
--all | Traite toutes les branches et tags | Pour un nettoyage complet |
Exemples pratiques détaillés
Section intitulée « Exemples pratiques détaillés »1. Supprimer un fichier spécifique
Section intitulée « 1. Supprimer un fichier spécifique »# Syntaxe de basegit filter-branch --force --index-filter \ 'git rm --cached --ignore-unmatch fichier-secret.txt' \ --prune-empty --tag-name-filter cat -- --all
# Explication de chaque partie :# --force : Force l'exécution# --index-filter : Utilise le filtre d'index (rapide)# git rm --cached : Supprime du staging (pas du disque)# --ignore-unmatch : Pas d'erreur si le fichier n'existe pas# --prune-empty : Supprime les commits devenus vides# --tag-name-filter cat : Préserve les noms de tags# -- --all : Traite toutes les branches2. Supprimer plusieurs fichiers
Section intitulée « 2. Supprimer plusieurs fichiers »# Méthode 1 : Pattern avec index-filtergit filter-branch --force --index-filter \ 'git rm --cached --ignore-unmatch *.key *.pem secrets.json config.py' \ --prune-empty --tag-name-filter cat -- --all
# Méthode 2 : Script bash complexegit filter-branch --force --index-filter ' git rm --cached --ignore-unmatch secrets.json git rm --cached --ignore-unmatch backup.sql git rm --cached --ignore-unmatch *.key git rm --cached --ignore-unmatch large-file.bin' --prune-empty --tag-name-filter cat -- --all3. Supprimer par taille de fichier
Section intitulée « 3. Supprimer par taille de fichier »# Supprimer tous les fichiers > 10MB avec tree-filtergit filter-branch --force --tree-filter \ 'find . -type f -size +10M -delete' \ --prune-empty --tag-name-filter cat -- --all
# Version plus sophistiquée avec logginggit filter-branch --force --tree-filter ' find . -type f -size +10M -print0 | while read -d $'\''\0'\'' file; do echo "Suppression de: $file" >&2 rm -f "$file" done' --prune-empty --tag-name-filter cat -- --all4. Nettoyer un répertoire spécifique
Section intitulée « 4. Nettoyer un répertoire spécifique »# Supprimer tout le contenu d'un dossiergit filter-branch --force --tree-filter \ 'rm -rf dossier-sensible/' \ --prune-empty --tag-name-filter cat -- --all
# Extraire uniquement un sous-répertoire (créer un nouveau dépôt)git filter-branch --force --subdirectory-filter mon-module/ \ --prune-empty --tag-name-filter cat -- --all5. Remplacer du contenu dans les fichiers
Section intitulée « 5. Remplacer du contenu dans les fichiers »# Remplacer des mots de passe dans tous les fichiersgit filter-branch --force --tree-filter ' find . -type f -name "*.py" -exec sed -i "s/password123/REMOVED/g" {} + find . -type f -name "*.js" -exec sed -i "s/api_key_secret/REMOVED/g" {} +' --prune-empty --tag-name-filter cat -- --allProcessus interne détaillé
Section intitulée « Processus interne détaillé »Ce qui se passe pendant l’exécution :
Section intitulée « Ce qui se passe pendant l’exécution : »- Backup automatique : Git sauvegarde les refs dans
refs/original/ - Parcours chronologique : Traite chaque commit dans l’ordre
- Application du filtre : Exécute la commande spécifiée
- Création nouveau commit : Avec le même auteur/date mais nouveau hash
- Mise à jour des refs : Pointe vers les nouveaux commits
Exemple de logs pendant l’exécution :
Section intitulée « Exemple de logs pendant l’exécution : »Rewrite 1a2b3c4d (1/47) (0 seconds passed, 46 remaining)Rewrite 2b3c4d5e (2/47) (1 seconds passed, 45 remaining)...Ref 'refs/heads/main' was rewrittenRef 'refs/heads/feature-branch' was rewrittenGestion des erreurs courantes
Section intitulée « Gestion des erreurs courantes »1. “Cannot create a new backup”
Section intitulée « 1. “Cannot create a new backup” »# Problème : Backup précédent existe# Solution : Forcer ou nettoyergit filter-branch --force ...
# Ou nettoyer manuellementrm -rf .git/refs/original/2. “Command failed”
Section intitulée « 2. “Command failed” »# Problème : Commande dans le filtre échoue# Solution : Ajouter gestion d'erreurgit filter-branch --index-filter \ 'git rm --cached --ignore-unmatch file.txt || true'3. Commits vides après filtrage
Section intitulée « 3. Commits vides après filtrage »# Problème : Des commits deviennent vides# Solution : Utiliser --prune-emptygit filter-branch --prune-empty ...4. Performance lente
Section intitulée « 4. Performance lente »# Problème : tree-filter trop lent# Solution : Utiliser index-filter quand possible# Au lieu de :git filter-branch --tree-filter 'rm -f secrets.txt'# Utiliser :git filter-branch --index-filter 'git rm --cached --ignore-unmatch secrets.txt'Post-traitement obligatoire
Section intitulée « Post-traitement obligatoire »Après chaque filter-branch :
Section intitulée « Après chaque filter-branch : »# 1. Nettoyer les référencesgit reflog expire --expire=now --all
# 2. Garbage collection agressivegit gc --prune=now --aggressive
# 3. Vérifier l'intégritégit fsck --full
# 4. Forcer la mise à jour du remotegit push --force-with-lease --allgit push --force-with-lease --tagsAlternatives modernes
Section intitulée « Alternatives modernes »git filter-repo (Recommandé)
Section intitulée « git filter-repo (Recommandé) »# Installationpip3 install git-filter-repo
# Utilisation (plus simple et rapide)git filter-repo --path secrets.txt --invert-pathsgit filter-repo --strip-blobs-bigger-than 10MComparaison des outils
Section intitulée « Comparaison des outils »| Critère | filter-branch | filter-repo | BFG |
|---|---|---|---|
| Vitesse | Lent | Rapide | Très rapide |
| Complexité | Élevée | Moyenne | Faible |
| Flexibilité | Maximale | Élevée | Limitée |
| Maintenance | Obsolète | Actif | Actif |
| Courbe d’apprentissage | Difficile | Moyenne | Facile |
Bonnes pratiques pour filter-branch
Section intitulée « Bonnes pratiques pour filter-branch »1. Préparation
Section intitulée « 1. Préparation »# Toujours créer une sauvegardecp -r mon-depot mon-depot-backup
# Travailler sur un clonegit clone --mirror original-repo.git cleaned-repo.gitcd cleaned-repo.git2. Test sur une branche
Section intitulée « 2. Test sur une branche »# Tester d'abord sur une branchegit checkout -b test-cleanupgit filter-branch --force --index-filter '...' HEAD~10..HEAD
# Si OK, appliquer sur tout l'historiquegit checkout maingit filter-branch --force --index-filter '...' --all3. Vérification
Section intitulée « 3. Vérification »# Vérifier que les secrets sont supprimésgitleaks detect --source . --verbose
# Vérifier la tailledu -sh .git/
# Vérifier l'intégritégit fsck --full4. Script de nettoyage complet
Section intitulée « 4. Script de nettoyage complet »#!/bin/bash# Script de nettoyage sécurisé
set -e # Arrêter en cas d'erreur
echo "🚨 ATTENTION: Cette opération est irréversible!"read -p "Continuer? (y/N): " -n 1 -rechoif [[ ! $REPLY =~ ^[Yy]$ ]]; then exit 1fi
# Sauvegardeecho "📦 Création de la sauvegarde..."cp -r . ../backup-$(date +%Y%m%d-%H%M%S)
# Nettoyageecho "🧹 Nettoyage en cours..."git filter-branch --force --index-filter \ 'git rm --cached --ignore-unmatch secrets.json config.py *.key' \ --prune-empty --tag-name-filter cat -- --all
# Post-traitementecho "🔧 Post-traitement..."git reflog expire --expire=now --allgit gc --prune=now --aggressive
# Vérificationecho "✅ Vérification avec GitLeaks..."if gitleaks detect --source . --exit-code; then echo "✅ Aucun secret détecté!"else echo "❌ Des secrets sont encore présents!" exit 1fi
echo "🎉 Nettoyage terminé avec succès!"Cas d’usage avancés
Section intitulée « Cas d’usage avancés »1. Diviser un dépôt
Section intitulée « 1. Diviser un dépôt »# Extraire un module en dépôt séparégit filter-branch --subdirectory-filter module-auth/ \ --prune-empty --tag-name-filter cat -- --all2. Fusionner des commits
Section intitulée « 2. Fusionner des commits »# Utiliser commit-filter pour fusionnergit filter-branch --commit-filter ' if [ "$GIT_COMMIT" = "commit-a-supprimer" ]; then skip_commit "$@" else git commit-tree "$@" fi' HEAD~10..HEAD3. Anonymiser l’historique
Section intitulée « 3. Anonymiser l’historique »# Remplacer tous les emailsgit filter-branch --env-filter ' export GIT_AUTHOR_EMAIL="anonymous@example.com" export GIT_COMMITTER_EMAIL="anonymous@example.com"' --all⚠️ Avertissements importants
Section intitulée « ⚠️ Avertissements importants »Risques majeurs :
Section intitulée « Risques majeurs : »- Irréversible : L’historique original est perdu
- Cassure des signatures : Les commits signés deviennent invalides
- Problèmes de collaboration : Force push nécessaire
- Références externes : Les liens vers les commits cassent
Quand NE PAS utiliser filter-branch :
Section intitulée « Quand NE PAS utiliser filter-branch : »- Sur un dépôt partagé en production
- Sans sauvegarde complète
- Si vous n’êtes pas sûr des conséquences
- Pour de petites modifications (utiliser
git revertà la place)