Git Filter-Repo est un outil polyvalent pour la réécriture de l'historique, qui comprend des capacités que je n'ai trouvées nulle part ailleurs. Il tombe à peu près dans le même espace d'outil que Git Filter-Branch, mais sans les mauvaises performances induisant la capitulation, avec beaucoup plus de capacités, et avec une conception qui évolue en termes d'utilisabilité au-delà des cas de réécriture triviale. Git Filter-Repo est désormais recommandé par le projet GIT au lieu de Git Filter-Branch.
Alors que la plupart des utilisateurs utiliseront probablement Filter-Repo comme un simple outil de ligne de commande (et n'utiliseront probablement que quelques-uns de ses drapeaux), à son cœur, Filter-Repo contient une bibliothèque pour créer des outils de réécriture d'historique. En tant que tels, les utilisateurs ayant des besoins spécialisés peuvent en tirer parti pour créer rapidement des outils de réécriture entièrement nouveaux.
filter-repo requires:
git-filter-repo
est un script Python à un seul fichier, ce qui a été fait pour effectuer une installation pour une utilisation de base sur de nombreux systèmes triviale: placez simplement ce fichier dans votre chemin $.
Voir installer.md pour des choses au-delà de l'utilisation de base ou des cas spéciaux. Les instructions les plus impliquées ne sont nécessaires que si l'un des éléments suivants s'applique:
Pour une documentation complète:
Si vous préférez apprendre des exemples:
Cela a été couvert plus en détail dans un article de Git Rev News sur Filter-Repo, mais quelques faits saillants pour les principaux concurrents:
Le filtre-branchage est extrêmement ralenti (plusieurs ordres de grandeur plus lents qu'il ne devrait l'être) pour les référentiels non triviaux.
Filter-Branch est criblé de gotchas qui peuvent corrompre silencieusement votre réécriture ou du moins contrecarrer vos efforts de "nettoyage" en vous donnant quelque chose de plus problématique et désordonné que ce que vous avez commencé.
Filtre-Branch est très onéreux à utiliser pour toute réécriture qui est même légèrement non triviale.
Le projet GIT a indiqué que les problèmes ci-dessus avec le filtre-branche ne peuvent pas être résolus de manière compatible; Ils vous recommandent de cesser d'utiliser le filtre-branche
Les fans inconditionnels de Filter-Branch peuvent être intéressés par Filter-Lalely (AKA Filter-Branch-ish), une réimplémentation de filtre-branchée basée sur Filter-Repo qui est plus performant (mais pas aussi rapide ou sûr que Filter- repo).
Une feuille de triche est disponible montrant comment convertir des exemples de commandes du manuel de filtre-branch en commandes filter-repo.
Excellent outil pour son temps, mais bien qu'il soit simple, il se limite à quelques types de réécritures.
Son architecture ne se prête pas à gérer plus de types de réécritures.
Son architecture présente certaines lacunes et bogues même pour son usecase prévu.
Les fans de BFG peuvent être intéressés par BFG-ish, une réimplémentation de BFG basé sur Filter-Repo qui comprend plusieurs nouvelles fonctionnalités et bugfixes par rapport à BFG.
Une feuille de triche est disponible montrant comment convertir des exemples de commandes du manuel de BFG Repo Cleaner en commandes Filter-Repo.
Disons que nous voulons extraire un morceau de référentiel, avec l'intention de fusionner simplement cette pièce dans un autre référentiel plus grand. Pour l'extraction, nous voulons:
Faire cela avec Filter-Repo est aussi simple que la commande suivante:
git filter-repo --path src/ --to-subdirectory-filter my-module --tag-rename ' ' : ' my-module- '
(Les citations simples ne sont pas nécessaires, mais indiquez plus clairement à un humain que nous remplaçons la chaîne vide en tant que préfixe avec my-module-
)
BFG Repo Cleaner n'est pas capable de ce type de réécriture; En fait, les trois types de changements recherchés sont en dehors de ses capacités.
Filtre-Branch est livré avec une pile de mises en garde (plus à ce sujet ci-dessous) même une fois que vous avez compris l'invocation nécessaire:
git filter-branch
--tree-filter ' mkdir -p my-module &&
git ls-files
| grep -v ^src/
| xargs git rm -f -q &&
ls -d *
| grep -v my-module
| xargs -I files mv files my-module/ '
--tag-name-filter ' echo "my-module-$(cat)" '
--prune-empty -- --all
git clone file:// $( pwd ) newcopy
cd newcopy
git for-each-ref --format= " delete %(refname) " refs/tags/
| grep -v refs/tags/my-module-
| git update-ref --stdin
git gc --prune=now
Certains pourraient remarquer que l'invocation de la branche filtrante ci-dessus sera vraiment lente en raison de l'utilisation de --tree-filtre; Vous pouvez également utiliser l'option - index-filtre de Filter-Branch, en modifiant les commandes ci-dessus pour:
git filter-branch
--index-filter ' git ls-files
| grep -v ^src/
| xargs git rm -q --cached;
git ls-files -s
| sed "s%$(printf \t)%&my-module/%"
| git update-index --index-info;
git ls-files
| grep -v ^my-module/
| xargs git rm -q --cached '
--tag-name-filter ' echo "my-module-$(cat)" '
--prune-empty -- --all
git clone file:// $( pwd ) newcopy
cd newcopy
git for-each-ref --format= " delete %(refname) " refs/tags/
| grep -v refs/tags/my-module-
| git update-ref --stdin
git gc --prune=now
Cependant, pour l'un ou l'autre commandement de la branche filtrante, il y a une pile de mises en garde. Tout d'abord, certains peuvent me demander pourquoi je lis dans cinq commandes ici pour le filtre-branche. Malgré l'utilisation de - all and --tag-name-filtre, et de la page manuelle de Filter-Branch affirmant qu'un clone est suffisant pour se débarrasser des anciens objets, les étapes supplémentaires pour supprimer les autres balises et faire un autre GC sont toujours nécessaires pour Nettoyez les anciens objets et évitez de mélanger une histoire nouvelle et ancienne avant de pousser quelque part. Autres mises en garde:
On peut en quelque sorte pirater ceci avec quelque chose comme:
git fast-export --no-data --reencode=yes --mark-tags --fake-missing-tagger
--signed-tags=strip --tag-of-filtered-object=rewrite --all
| grep -vP ' ^M [0-9]+ [0-9a-f]+ (?!src/) '
| grep -vP ' ^D (?!src/) '
| perl -pe ' s%^(M [0-9]+ [0-9a-f]+ )(.*)$%1my-module/2% '
| perl -pe ' s%^(D )(.*)$%1my-module/2% '
| perl -pe s%refs/tags/%refs/tags/my-module-%
| git -c core.ignorecase=false fast-import --date-format=raw-permissive
--force --quiet
git for-each-ref --format= " delete %(refname) " refs/tags/
| grep -v refs/tags/my-module-
| git update-ref --stdin
git reset --hard
git reflog expire --expire=now --all
git gc --prune=now
Mais cela vient avec des mises en garde et des limites désagréables:
Aucun des outils de filtrage du référentiel existant n'a fait ce que je voulais; Ils ont tous échoué pour mes besoins. Aucun outil n'a fourni aucun des huit premiers traits ci-dessous, et aucun outil n'a fourni plus de deux des quatre derniers traits:
[Rapport de démarrage] Fournir à l'utilisateur une analyse de leur référentiel pour les aider à démarrer ce qu'il faut tailler ou renommer, au lieu de s'attendre à ce qu'ils devinent ou trouvent d'autres outils pour le comprendre. (Déclenché, par exemple en fonctionnant la première fois avec un drapeau spécial, comme --analyze.)
[Garder vs Suppter] Au lieu de simplement fournir aux utilisateurs un moyen de supprimer facilement les chemins sélectionnés, fournit également des indicateurs pour que les utilisateurs ne conservent que certains chemins. Bien sûr, les utilisateurs pourraient solution en spécifiant pour supprimer tous les chemins autres que ceux qu'ils souhaitent conserver, mais la nécessité de spécifier tous les chemins qui ont jamais existé dans n'importe quelle version du référentiel pourrait parfois être assez douloureux. For filter-branch, using pipelines like git ls-files | grep -v ... | xargs -r git rm
pourrait être une solution de contournement raisonnable mais peut devenir lourde et n'est pas aussi simple pour les utilisateurs; De plus, ces commandes sont souvent spécifiques au système de fonctionnement (pouvez-vous repérer le gnusme dans l'extrait que j'ai fourni?).
[Renommer] Il devrait être facile de renommer les chemins. Par exemple, en plus de permettre à un de traiter un sous-répertoire comme la racine du référentiel, offre également aux utilisateurs des options pour que la racine du référentiel devienne un sous-répertoire. Et plus généralement, les fichiers et les répertoires sont facilement renommés. Fournissez des vérifications de la santé mentale si le renommage provoque plusieurs fichiers sur le même chemin. (Et ajouter une manipulation spéciale de sorte que si un engagement a simplement copié Oldname-> newname sans modification, alors filtrant Oldname-> newname ne déclenche pas le contrôle de santé mentale et meurt sur cette validation.)
[Sécurité plus intelligente] L'écriture de copies des références originales vers un espace de noms spécial dans le dépôt ne fournit pas de mécanisme de récupération convivial. Beaucoup auraient du mal à récupérer en utilisant cela. Presque tous ceux que j'ai jamais vus faire une opération de filtrage du référentiel l'ont fait avec un nouveau clone, car effacer le clone en cas d'erreur est un mécanisme de récupération beaucoup plus facile. Encouragez fortement ce flux de travail en détectant et en faisant du renflouement si nous ne sommes pas dans un nouveau clone, sauf si l'utilisateur l'emporte sur - Force.
[Rétractation automatique] Retirez automatiquement l'ancien cruft et reconditionnez le référentiel pour l'utilisateur après le filtrage (sauf s'il est remplacé); Cela simplifie les choses pour l'utilisateur, aide à éviter de mélanger les anciens et les nouveaux histoires, et évite les problèmes où le processus en plusieurs étapes pour réduire le référentiel documenté dans la page manuelle ne fonctionne pas réellement dans certains cas. (Je te regarde, filtre-branche.)
[Séparation propre] Évitez de confondre les utilisateurs (et empêchez le re-push accidentel de vieilles choses) en raison du mélange de vieux référentiels et réécrits ensemble. (Ceci est particulièrement un problème avec le filtre-branchage lors de l'utilisation de l'option --tag-nom-filtre, et parfois aussi un problème lors du filtrage d'un sous-ensemble de branches.)
[Polyvalence] Fournir à l'utilisateur la possibilité d'étendre l'outil ou même d'écrire de nouveaux outils qui tirent parti des capacités existantes, et de fournir cette extensibilité d'une manière qui (a) évite la nécessité de fournir des processus séparés (qui détruiraient les performances), (b) Évite de faire en sorte que l'utilisateur spécifie les commandes de shell dépendant du système d'exploitation (ce qui empêcherait les utilisateurs de partager les commandes entre eux), (c) profite des structures de données riches (parce que les hachages, les dictons, les listes et les tableaux sont prohibitifs dans le shell) et (( d) Fournit des capacités de manipulation de cordes raisonnables (qui manquent cruellement de coquille).
[Anciennes références de validation] Fournissent aux utilisateurs un moyen d'utiliser les anciens ID de validation avec le nouveau référentiel (en particulier via la mappage de l'ancien aux nouveaux hachages avec des références / Remplacement / Références).
[Commit Message Cohérence] Si les messages de validation se réfèrent à d'autres engins par ID (par exemple "Ce revers Commit 01234567890ABCDEF", "Dans Commit 0013Deadbeef9a ..."), ces messages de validation doivent être réécrits pour se référer aux nouveaux ID de validation.
[Devenir vide de l'élagage] Les engagements qui deviennent vides en raison du filtrage doivent être taillés. Si le parent d'une validation est taillé, le premier ancêtre non étiré doit devenir le nouveau parent. Si aucun ancêtre non étiré n'existe et que le commit n'était pas une fusion, alors il devient un nouvel engagement fondamental. Si aucun ancêtre non élaboré n'existe et que le commit était une fusion, alors la fusion aura un parent de moins (et rendra ainsi probablement un commit non-fusion qui serait lui-même taillé s'il n'avait pas de changements de dossier à part) . Une chose particulière à noter ici est que nous élaguons les commits qui deviennent vides, pas les engagements qui commencent vide. Some projects intentionally create empty commits for versioning or publishing reasons, and these should not be removed. (En tant que cas spécial, les engagements qui ont commencé vide mais dont le parent a été taillé sera également considéré comme étant "devenu vide".)
[Devenir l'élagage à dégénérer] l'élagage des engagements qui deviennent vides peut potentiellement provoquer des changements de topologie, et il y a beaucoup de cas particuliers. Normalement, les commits de fusion ne sont pas supprimés car ils sont nécessaires pour préserver la topologie du graphique, mais l'élagage des parents et d'autres ancêtres peut finalement entraîner la perte d'un ou plusieurs parents. Un cas simple a déjà été noté ci-dessus: si un engagement de fusion perd suffisamment de parents pour devenir un engagement non-fusion et qu'il n'a pas de modifications de fichier, alors il peut également être élaqué. Les engagements de fusion peuvent également avoir une topologie qui devient dégénérée: elle pourrait se retrouver avec la Merge_Base en tant que parents (si tous les engagements intermédiaires du repo d'origine étaient taillés), ou il pourrait se retrouver avec un parent qui est un ancêtre de son autre mère. Dans de tels cas, si la fusion n'a pas de modifications de fichiers qui lui est propre, la commission de fusion peut également être taillée. Cependant, comme nous le faisons avec l'élagage vide, nous ne protons pas les engagements de fusion qui ont commencé dégénérés (ce qui indique qu'il peut être intentionnel, comme avec les fusions --no-f le leur.
[Speed] Le filtrage doit être raisonnablement rapide
Voir les directives contributives.
Les participants à la communauté Filter-Repo devraient adhérer aux mêmes normes que pour le projet GIT, de sorte que le code de conduite GIT s'applique.
Les travaux sur Filter-Repo et son prédécesseur ont également motivé de nombreuses améliorations à l'export rapide et à l'importance rapide (et parfois d'autres commandes) dans Core Git, en fonction des choses que Filter-Repo doit faire son travail: