O GIT Filter-Repo é uma ferramenta versátil para reescrever o histórico, que inclui recursos que não encontrei em nenhum outro lugar. Ele se enquadra aproximadamente no mesmo espaço de ferramenta que o Git Filtro Chranch, mas sem o mau desempenho indutor de capitulação, com muito mais recursos e com um design que escala em termos de usabilidade além dos casos de reescrita trivial. O filtro Git-Repo agora é recomendado pelo projeto Git, em vez de git filtro rale.
Embora a maioria dos usuários provavelmente use apenas o filtro-repo como uma ferramenta simples de linha de comando (e provavelmente use apenas alguns de seus sinalizadores), em seu principal filtro-repo contém uma biblioteca para criar ferramentas de reescrita de histórico. Como tal, os usuários com necessidades especializadas podem aproveitá -lo para criar rapidamente ferramentas de reescrita de histórico totalmente novas.
Filter-repo requer:
git-filter-repo
é um script python de arquivo único, que foi feito para fazer a instalação para uso básico em muitos sistemas triviais: basta colocar esse arquivo no seu $ PATH.
Consulte Install.md para coisas além do uso básico ou de casos especiais. As instruções mais envolvidas são necessárias apenas se um dos seguintes se aplicar:
Para documentação abrangente:
Se você preferir aprender com exemplos:
Isso foi abordado com mais detalhes em um artigo do Git Rev News sobre Filter-Repo, mas alguns destaques para os principais concorrentes:
O Filtro-Branch é extremamente indiscutivelmente lento (múltiplas ordens de magnitude mais lentamente do que deveria) para repositórios não triviais.
O Filter-Cranch está repleto de gotas que podem corromper silenciosamente sua reescrita ou pelo menos frustrar seus esforços de "limpeza", dando a você algo mais problemático e confuso do que o que você começou.
O filtro é muito oneroso de usar para qualquer reescrita que seja um pouco não trivial.
O projeto Git afirmou que os problemas acima com o ramo de filtro não podem ser fixos compatíveis com compactação; Eles recomendam que você pare de usar o ramo de filtro
Os fãs obstinados do Filtro-Branch podem estar interessados em filtrar-lamely (também conhecida como ramo de filtro), uma reimplementação de ramo de filtro baseada em filtro-repO, que é mais executiva (embora não tão rápido ou seguro quanto o filtro- repo).
Uma folha de dicas está disponível mostrando como converter comandos de exemplo do manual do filtro-ramo em comandos filtro-repo.
Ótima ferramenta para o seu tempo, mas, embora simplifique algumas coisas, é limitada a alguns tipos de reescritas.
Sua arquitetura não é passível de lidar com mais tipos de reescritas.
Sua arquitetura apresenta algumas deficiências e insetos, mesmo para a base de US $ pretendida.
Os fãs do BFG podem estar interessados no BFG-ISH, uma reimplementação de BFG com base no Filtro-Repo, que inclui vários novos recursos e bugs em relação ao BFG.
Uma folha de dicas está disponível mostrando como converter comandos de exemplo do manual do limpador de repositório BFG em comandos filtro-repo.
Digamos que queremos extrair um pedaço de repositório, com a intenção de fundir exatamente essa peça em outro repositório maior. Para extração, queremos:
Fazer isso com o filtro-repo é tão simples quanto o seguinte comando:
git filter-repo --path src/ --to-subdirectory-filter my-module --tag-rename ' ' : ' my-module- '
(As citações únicas são desnecessárias, mas tornam mais claro para um humano que estamos substituindo a corda vazia como um prefixo por my-module-
)
O limpador de repositório do BFG não é capaz desse tipo de reescrita; De fato, todos os três tipos de alterações desejadas estão fora de suas capacidades.
O Filtro-Branch vem com uma pilha de advertências (mais sobre isso abaixo), mesmo depois de descobrir as invocação (s) necessária (s):
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
Alguns podem perceber que a invocação de ramo de filtro acima será realmente lenta devido ao uso de filtro de árvore; Como alternativa
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
No entanto, para qualquer comando de filtro, há uma pilha de advertências. Primeiro, alguns podem estar se perguntando por que listo cinco comandos aqui para o filtro rale. Apesar do uso do filtro de filtro de names e-e do ramo de filtro, alegando que um clone é suficiente para se livrar de objetos antigos, as etapas extras para excluir as outras tags e fazer outro GC ainda são necessárias para Limpe os objetos antigos e evite misturar a história nova e antiga antes de empurrar em algum lugar. Outras advertências:
Pode -se meio que hackear isso junto com algo como:
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
Mas isso vem com algumas advertências e limitações desagradáveis:
Nenhuma das ferramentas de filtragem de repositório existente fez o que eu queria; Todos eles surgiram para minhas necessidades. Nenhuma ferramenta forneceu nenhuma das oito primeiras características abaixo que eu queria, e nenhuma ferramenta forneceu mais de duas das últimas quatro características:
[Relatório inicial] Forneça ao usuário uma análise de seu repositório para ajudá -los a começar o que podar ou renomear, em vez de esperar que eles adivinhem ou encontrem outras ferramentas para descobrir isso. (Acionado, por exemplo, executando a primeira vez com uma bandeira especial, como -Analyze.)
[Mantenha vs. Remover] Em vez de apenas fornecer uma maneira de os usuários removerem facilmente os caminhos selecionados, também fornecem sinalizadores para os usuários manterem apenas determinados caminhos. Certamente, os usuários podem alternar isso especificando para remover todos os caminhos que não desejam manter, mas a necessidade de especificar todos os caminhos que já existiram em qualquer versão do repositório às vezes poderia ser bastante dolorosa. Para o ramo de filtro, usando pipelines como git ls-files | grep -v ... | xargs -r git rm
pode ser uma solução razoável, mas pode ficar pesada e não é tão direta para os usuários; Além disso, esses comandos costumam ser específicos do sistema operacional (você pode identificar o gNuísmo no trecho que eu forneci?).
[Renomeação] Deve ser fácil renomear caminhos. Por exemplo, além de permitir que alguém trate algum subdiretório como a raiz do repositório, também fornece opções para os usuários tornarem a raiz do repositório apenas se tornar um subdiretório. E geralmente permitem que arquivos e diretórios sejam facilmente renomeados. Forneça verificações de sanidade se a renomeação faz com que vários arquivos existam no mesmo caminho. (E adicione um manuseio especial para que, se um commit apenas copiar o nome da velha-> newName sem modificação, filtrando o nome do Oldname-> Nome não aciona a verificação da sanidade e morre com esse compromisso.)
[Segurança mais inteligente] Escrever cópias dos árbitros originais para um espaço de nome especial no repositório não fornece um mecanismo de recuperação amigável. Muitos lutariam para se recuperar usando isso. Quase todo mundo que eu já vi fazer uma operação de filtragem de repositório o fez com um clone novo, porque limpar o clone em caso de erro é um mecanismo de recuperação muito mais fácil. Incentive fortemente esse fluxo de trabalho detectando e resgatando se não estivermos em um clone novo, a menos que o usuário substitua -force.
[Encolhimento automático] remova automaticamente o cruzamento antigo e reembale o repositório para o usuário após a filtragem (a menos que substitua); Isso simplifica as coisas para o usuário, ajuda a evitar a mistura de histórico antigo e novo e evita problemas em que o processo de várias etapas para diminuir o repositório documentado na Manpage não funciona em alguns casos. (Estou olhando para você, ramo de filtro.)
[Separação limpa] Evite confundir usuários (e impedir a renovação acidental de coisas antigas) devido à mistura de repositórios antigos e reescrito. (Isso é particularmente um problema com o Filtro Chranch ao usar a opção de filtro de tag-name e, às vezes, também um problema ao filtrar apenas um subconjunto de ramificações.)
[Versatility] fornece ao usuário a capacidade de estender a ferramenta ou até escrever novas ferramentas que aproveitam os recursos existentes e fornecem essa extensibilidade de uma maneira que (a) evita a necessidade de destacar processos separados (que destruiriam o desempenho), (b) evita fazer com que o usuário especifique os comandos do shell dependente de OS (o que impediria os usuários de compartilhar comandos um com o outro), (c) aproveita as estruturas de dados ricas (porque hashes, ditos, listas e matrizes são proibitivamente difíceis no shell) e ( d) Fornece recursos razoáveis de manipulação de cordas (que não têm muito tempo).
[Referências antigas de compromisso] Forneça uma maneira de os usuários usarem IDs de comprometimento antigos com o novo repositório (em particular através do mapeamento de antigos para novos hashes com referências/ referências/ referências).
[Confirme consistência da mensagem] Se as mensagens de confirmação se referirem a outras confirmações por ID (por exemplo, "Isso reverte com compromisso 01234567890ABCDEF", "No Commit 0013DeadBeef9a ..."), essas mensagens de comprometimento devem ser reescritas para se referir aos novos IDs de confirmação.
[A poda de tornar-se vazia] Cometidos que ficam vazios devido à filtragem devem ser podados. Se o pai de uma confirmação for podado, o primeiro ancestral não contratado precisa se tornar o novo pai. Se não existe ancestral não corrigido e o compromisso não era uma mesclagem, ele se torna um novo commit raiz. Se não existir ancestral não cortado e o compromisso era uma mesclagem, a mesclagem terá menos um pai (e, assim, tornará provável que se torne um compromisso não mérito que seria podado se não tivesse mudanças próprias de arquivo) . Uma coisa especial a ser observada aqui é que podamos cometidos que ficam vazios, não cometidos que começam vazios. Alguns projetos criam intencionalmente comprometimentos vazios para versões ou publicações, e estes não devem ser removidos. (Como um caso especial, começos que começaram vazios, mas cujos pais foram removidos também serão considerados como "ficaram vazios".)
[Tornar-se degenerar a poda] A poda de commits que ficam vazios pode potencialmente causar mudanças de topologia, e há muitos casos especiais. Normalmente, os compromissos de Merge não são removidos, pois são necessários para preservar a topologia de gráficos, mas a poda dos pais e outros ancestrais pode resultar na perda de um ou mais pais. Um caso simples já foi observado acima: se uma confirmação de mesclagem perde pais suficientes para se tornar uma confirmação não mércia e não possui alterações de arquivo, também pode ser podado. Os compromissos de Merge também podem ter uma topologia que se torna degenerada: ela pode acabar com a Merge_base servindo como ambos os pais (se todos os que intervieram com o repo original fossem podados), ou poderia acabar com um pai que é um ancestral de seu outro pai. Nesses casos, se a mesclagem não tiver alterações próprias de arquivo, o comprometimento da mesclagem também poderá ser podado. No entanto, por mais que façamos com a poda vazia, não podamos os compromissos de mescla eles próprios.
[Speed] A filtragem deve ser razoavelmente rápida
Veja as diretrizes contribuintes.
Espera-se que os participantes da comunidade filtro-repO sigam os mesmos padrões do projeto Git, para que o código de conduta Git se aplique.
O trabalho em filtro-repo e seu antecessor também impulsionou inúmeras melhorias no Exportação Fast e Fast-Import (e, ocasionalmente, outros comandos) no Git Core, com base nas coisas que o filtro-repo precisa fazer seu trabalho: