Como lidar com repositórios grandes com o Git

Nicola Paolucci
Nicola Paolucci
Voltar para a lista

O git é uma escolha fantástica para acompanhar a evolução da sua base de código e colaborar de forma eficiente com seus colegas. Mas o que acontece quando o repositório que você quer rastrear é muito grande?

Nesta publicação, vou tentar dar algumas ideias e técnicas para a abordagem adequada das diferentes categorias de grandeza.

Duas categorias de repositórios grandes

Se você parar para pensar, existem duas razões principais para os repositórios terem um crescimento massivo:

  • Eles acumulam um histórico muito longo (o projeto cresce por um período prolongado e a bagagem se acumula)
  • Eles incluem enormes ativos binários que precisam ser rastreados e emparelhados com o código.
  • Ambas as anteriores.

Assim, um repositório pode crescer em duas direções ortogonais: o tamanho do diretório de trabalho (ou seja, o último commit) e o tamanho de todo o histórico acumulado.

Às vezes, o segundo tipo de problema é agravado pelo fato de que artefatos binários antigos ainda estão armazenados no repositório, mas há uma correção um pouco fácil — embora irritante — veja abaixo.

Para os dois cenários acima, as técnicas e soluções alternativas são diferentes (embora às vezes complementares) e, portanto, vou abordar um de cada vez.

Manipulação de repositórios com um histórico muito longo

Mesmo que os limites que identificam um repositório como massivo sejam bastante altos (por exemplo, o último kernel do Linux registra mais de 15 milhões de linhas de código, mas as pessoas parecem não se importar de examinar tudo), projetos muito antigos que, por razões regulatórias/legais têm que ser mantidos intactos podem ser um desafio para a clonagem (agora, para ser transparente, o kernel do Linux é dividido em um repositório histórico e um mais recente e requer uma configuração de enxerto simples para ter acesso ao histórico unificado completo).

A solução simples é um clone superficial

A primeira solução para um clone rápido e para economizar tempo e espaço em disco para desenvolvedores e sistemas é executar um clone superficial usando o git. Um clone superficial permite clonar um repositório mantendo apenas os n commits mais recentes do histórico.

Como se usa esse recurso? Basta usar a opção - -depth, por exemplo:

git clone --depth depth remote-url

Imagine que você acumulou dez ou mais anos de histórico de projetos no repositório (por exemplo, para o Jira, a gente migrou para o git uma base de código de 11 anos). A economia de tempo pode ir aumentando até ser muito perceptível.

O clone completo do Jira tem 677 MB, com o diretório de trabalho sendo outros 320+MB, compensando mais de 47.000 commits. A partir de uma verificação rápida no checkout do Jira, um clone superficial levou 29,5 segundos em comparação com os 4 minutos e 24 segundos de um clone completo com todo o histórico. A disparidade também cresce em proporção a quantos ativos binários seu projeto engoliu ao longo do tempo. Em qualquer caso, os sistemas de build também podem se beneficiar muito com essa técnica.

O Git recente melhorou o suporte para clones superficiais

Os clones superficiais costumavam ser cidadãos um pouco prejudicados do mundo git, pois algumas operações mal eram suportadas. Mas as versões recentes (1.9+) melhoraram muito a situação e você envia pull e push para os repositórios, mesmo a partir de um clone superficial agora.

A solução parcial é o filter-branch

Para os enormes repositórios que têm grandes cruft binários com commit por engano ou ativos antigos não são mais necessários, uma ótima solução é usar filter-branch. O comando permite percorrer todo o histórico do projeto filtrando, massageando, modificando, pulando arquivos de acordo com padrões predefinidos. É uma ferramenta muito poderosa no seu arsenal git. Já existem scripts auxiliares disponíveis para identificar objetos grandes no repositório do Git, o que deve facilitar o suficiente.

Exemplo de uso de filter-branch (crédito):

git filter-branch --tree-filter 'rm -rf /path/to/spurious/asset/folder' HEAD

filter-branch tem uma pequena desvantagem: depois de usar filter-branch, você reescreve todo o histórico do seu projeto: todos os IDs de commit mudam. Assim, é preciso que todos os desenvolvedores reclonem o repositório atualizado.

Então, se você está planejando realizar uma ação de limpeza usando o filter-branch, deve alertar sua equipe, planejar um pequeno congelamento enquanto a operação é realizada e, em seguida, notificar a todos que eles devem clonar o repositório de novo.

Alternativa ao clone superficial: Clone apenas um branch

Desde o git 1.7.10, de abril de 2012, você também pode limitar a quantidade de histórico clonado fazendo a clonagem de uma única ramificação, como mostrado aqui:

git clone URL --branch branch_name --single-branch [folder]

Esse hack específico seria útil para ramificações divergentes e de longa duração ou se você tiver muitas ramificações. Se você tiver apenas algumas ramificações com poucas diferenças, provavelmente não vai ver uma grande diferença ao usar essa estratégia.

Referência do Stack Overflow.

Gerenciando repositórios com enormes arquivos binários

A segunda categoria de grandes repositórios é composta por bases de código que precisam rastrear enormes ativos binários. As equipes de jogos precisam lidar com enormes modelos 3D, as equipes de desenvolvimento da Web podem precisar rastrear ativos de imagem bruta, as equipes de CAD podem precisar manipular e rastrear o status dos resultados binários. Portanto, existem diferentes categorias de equipe de software que enfrentam esse problema com o git.

O Git não é muito ruim no manuseio de ativos binários, mas também não é lá muito bom. Por padrão, o git vai compactar e armazenar todas as versões completas subsequentes dos ativos binários, o que não é o ideal se você tiver muitos.

Existem alguns ajustes básicos que melhoram a situação, como executar a coleta de lixo git gc, ou ajustar o uso de commits delta para alguns tipos binários em .gitattributes.

Mas é importante refletir sobre a natureza dos ativos binários do projeto, pois a abordagem vencedora pode variar. Por exemplo, aqui estão três pontos a serem verificados (obrigado a Stefan Saasen pelas observações):

  • Para arquivos binários que mudam muito - e não apenas alguns cabeçalhos de metadados - é provável que a compactação delta vai ser inútil, então a sugestão é desligar o delta para esses arquivos para evitar o trabalho desnecessário de compactação delta como parte do reempacotamento
  • No cenário acima, é provável que esses arquivos também não sejam compactados com zlib, então você pode desativar a compactação com core.compression 0 ou core.loosecompression 0; essa é uma configuração global que prejudicaria todos os arquivos não binários que compactam bem, então a sugestão faz sentido se você dividir os ativos binários em um repositório separado.
  • É importante lembrar que o git gc transforma os objetos" soltos" duplicados em um único arquivo de pacote, mas, a menos que os arquivos sejam compactados de alguma forma que talvez não faça nenhuma diferença significativa na relação com o arquivo de pacote resultante.
  • Explore o ajuste do core.bigFileThreshold. Qualquer coisa maior que 512 MiB não vai ser compactada em delta de qualquer maneira - sem ter que definir .gitattributes - então talvez isso seja algo que valha a pena ajustar.

Técnica 1: checkout esparso

Uma ajuda leve para o problema de ativos binários é o checkout esparso (disponível desde o Git 1.7.0]). Essa técnica permite manter o diretório de trabalho limpo, detalhando quais pastas você deseja preencher. Essa ação não afeta o tamanho do repositório local geral, mas pode ser útil se você tiver uma enorme árvore de pastas.

Quais são os comandos envolvidos? Aqui está um exemplo (crédito):

  • Clone o repositório completo uma vez: git clone< repository-address>
  • Ative o recurso: git config core.sparsecheckout true
  • Adicione pastas que necessárias, ignorando as pastas de ativos:
echo src/ › .git/info/sparse-checkout
  • Leia a árvore conforme especificado: git read-tree -m -u HEAD

Depois do exposto acima, você pode voltar a usar seus comandos git normais, mas o diretório de trabalho vai conter apenas as pastas especificadas acima.

Técnica 2: uso de submódulos

Outra maneira de lidar com enormes pastas de arquivo binário é dividi-las em um repositório separado e extrair os ativos no projeto principal usando submódulos. Isso lhe dá uma maneira de controlar quando você atualiza os ativos. Veja mais sobre submódulos nestas postagens: conceito central, dicas e alternativas.

Se seguir o caminho dos submódulos, você pode querer verificar as complexidades de lidar com as dependências do projeto, uma vez que algumas das abordagens possíveis para o problema dos binários grandes podem ser ajudadas pelas abordagens que eu menciono lá.

Técnica 3: usar git annex ou git-bigfiles

Uma terceira opção para lidar com arquivos binários com o git é confiar em uma extensão adequada de terceiros.

O primeiro que menciono é o git-annex, que permite gerenciar arquivos binários com o git sem verificar o conteúdo do arquivo no repositório. O git-annex salva os arquivos em um armazenamento especial de chave-valor e apenas links simbólicos são então verificados no git e versionados como arquivos comuns. É simples de usar e os exemplos são autoexplicativos.

A segunda é o git-bigfiles, um git fork que espera tornar a vida suportável para as pessoas que usam o Git em projetos que hospedam arquivos muito grandes.

[ATUALIZAÇÃO] … ou você pode pular tudo o que foi dito e usar o Git LFS

Se você costuma trabalhar com arquivos grandes, a melhor solução pode ser aproveitar o suporte a arquivos grandes (LFS) desenvolvido pela Atlassian em parceria com o GitHub em 2015.

Git LSF é uma extensão que armazena ponteiros para arquivos grandes no repositório, em vez de armazenar os arquivos em si. Os arquivos reais são armazenados em um servidor remoto. Como você pode imaginar, isso reduz demais o tempo necessário para clonar seu repositório.

O Bitbucket é compatível com Git LFS, assim como o GitHub. Então, é provável que você já tenha acesso a essa tecnologia. É muito útil para equipes que incluem designers, cinegrafistas, músicos ou usuários de CAD.

Conclusão

Não desista dos recursos fantásticos do git só porque você tem um enorme histórico de repositório ou ativos enormes. Existem soluções viáveis para os dois problemas.

Siga as minhas publicações em @durdn para mais DVCS arrasadores.

Pronto(a) para aprender Git?

Tente este tutorial interativo.

Comece agora mesmo