Partial clone: baixando apenas o necessário de repositórios grandes

1. O problema dos repositórios monolíticos e a necessidade de clonagem seletiva

1.1. Crescimento descontrolado

Repositórios Git crescem rapidamente ao longo do tempo. Cada commit adiciona objetos ao armazenamento local. Quando o projeto inclui binários, imagens, arquivos de dependências compiladas ou históricos extensos de código, o tamanho do repositório pode facilmente ultrapassar gigabytes. Um clone completo de um monorepo com ativos de jogos, por exemplo, pode consumir mais de 10 GB de espaço em disco.

1.2. Impacto no fluxo de trabalho

Clonar um repositório enorme impacta diretamente a produtividade:
- Tempo de clone: minutos ou até horas em conexões lentas
- Espaço em disco: necessidade de armazenar todo o histórico localmente
- Desempenho: comandos como git log, git status e git diff tornam-se lentos ao processar milhares de objetos

1.3. Alternativas tradicionais e suas limitações

Antes do partial clone, existiam duas abordagens principais:

Shallow clone (--depth 1): baixa apenas o commit mais recente, sem histórico. Limitação: impossibilita navegar pelo histórico, fazer merge de branches antigas ou usar git blame completo.

Sparse checkout: permite baixar apenas subconjuntos de arquivos, mas ainda exige que todos os objetos do histórico estejam presentes localmente. Não resolve o problema de espaço para repositórios com muitos binários.

2. Conceitos fundamentais do Partial Clone

2.1. O que é um partial clone

Partial clone é um recurso do Git que permite clonar um repositório sem baixar todos os objetos imediatamente. Em vez disso, apenas os objetos essenciais para a operação inicial são transferidos. Objetos restantes são baixados sob demanda, conforme necessário.

2.2. Promisor objects e promisor remotes

O Git usa dois conceitos chave:

  • Promisor objects: objetos que o Git sabe que existem no servidor remoto, mas ainda não estão armazenados localmente. São como "promessas" de que o objeto será entregue quando solicitado.
  • Promisor remote: o repositório remoto configurado como fonte confiável para buscar esses objetos prometidos. Por padrão, é o origin.

Quando você executa git checkout em um branch cujos arquivos não foram baixados, o Git automaticamente contacta o promisor remote para obter os blobs necessários.

2.3. Diferença entre partial clone, shallow clone e blobless clone

Tipo O que baixa Histórico disponível
Clone completo Todos os objetos Completo
Shallow clone Apenas commits recentes Limitado (profundidade definida)
Blobless clone (partial) Commits e trees, sem blobs Completo (blobs sob demanda)
Treeless clone (partial) Apenas commits Completo (trees e blobs sob demanda)

3. Modos de partial clone: blobless, treeless e full

3.1. --filter=blob:none

O modo mais comum. Baixa todos os commits e trees (estrutura de diretórios), mas nenhum blob (conteúdo de arquivos). Quando você faz checkout de um arquivo, o Git baixa apenas o blob daquele arquivo específico.

git clone --filter=blob:none https://github.com/exemplo/repositorio-grande.git

3.2. --filter=tree:0

Modo mais agressivo. Baixa apenas os commits, sem trees nem blobs. Cada operação que precisa da estrutura de diretórios (como git status) exige download de trees adicionais.

git clone --filter=tree:0 https://github.com/exemplo/repositorio-grande.git

3.3. --filter=sparse:oid=<blob>

Filtro personalizado baseado em um arquivo de especificação. Permite definir regras complexas sobre quais objetos baixar. Exige um blob específico no repositório contendo as regras de filtragem.

git clone --filter=sparse:oid=abc123def456 https://github.com/exemplo/repositorio-grande.git

4. Configurando e clonando com partial clone

4.1. Comando prático

O exemplo mais comum para repositórios grandes:

git clone --filter=blob:none https://github.com/exemplo/monorepo.git
cd monorepo

Neste momento, você tem todo o histórico de commits e a estrutura de diretórios, mas nenhum conteúdo de arquivo. O clone é rápido e ocupa pouco espaço.

4.2. Configurando um repositório existente

Se você já tem um clone completo e quer convertê-lo para partial clone:

git fetch --filter=blob:none origin
git config remote.origin.promisor true
git config remote.origin.partialclonefilter blob:none

Agora, objetos antigos podem ser removidos com git gc.

4.3. Verificando o estado do clone

Para ver quantos objetos estão faltando:

git rev-list --objects --all --count
git rev-list --objects --all --missing=print | wc -l

O primeiro comando mostra o total de objetos no histórico. O segundo mostra quantos estão faltando localmente.

5. Trabalhando com um repositório parcialmente clonado

5.1. Acesso sob demanda

Ao fazer checkout de um branch:

git checkout main

O Git automaticamente baixa os blobs dos arquivos modificados no commit atual. Se você tem 10.000 arquivos, mas apenas 50 foram alterados, apenas 50 blobs são baixados.

5.2. Comportamento de comandos comuns

  • git log: funciona normalmente, pois só precisa de commits e trees (já baixados no modo blobless)
  • git blame arquivo.txt: baixa o blob do arquivo e os blobs de versões anteriores necessários para o blame
  • git status: funciona sem baixar blobs adicionais (usa apenas trees)
  • git diff HEAD~1 HEAD: baixa apenas os blobs dos arquivos que mudaram entre os dois commits

5.3. Limitações

Comandos que exigem todos os objetos para funcionar corretamente podem falhar ou ser extremamente lentos:

  • git gc --aggressive sem filtro pode tentar baixar todos os objetos
  • git fsck pode reportar objetos faltantes (o que é esperado em partial clones)
  • Ferramentas de análise de código que escaneiam todo o repositório podem forçar downloads massivos

6. Gerenciamento e manutenção do clone parcial

6.1. Fetch em partial clones

O git fetch padrão funciona como esperado, baixando novos commits e trees, mas não blobs desnecessários:

git fetch origin

Para baixar apenas objetos prometidos específicos:

git fetch --filter=blob:none origin main

6.2. Convertendo para clone completo

Se precisar de todos os objetos localmente (por exemplo, para arquivar o repositório):

git fetch --refetch origin

Isso baixa todos os objetos faltantes do remoto.

6.3. Limpeza de objetos baixados

Objetos baixados para operações temporárias podem ser removidos:

git gc --filter=blob:none

Isso remove blobs que não são mais referenciados por commits locais, mantendo apenas o necessário.

7. Casos de uso e boas práticas

7.1. Monorepos com binários grandes

Empresas de jogos (Unity, Unreal) usam partial clone para gerenciar ativos como texturas, modelos 3D e áudio. Desenvolvedores baixam apenas os assets relevantes para sua área.

7.2. CI/CD pipelines

Em pipelines de integração contínua, o tempo de setup é crítico:

# Pipeline de CI
git clone --filter=blob:none --single-branch --branch $BRANCH $REPO_URL

Isso reduz o tempo de clone de minutos para segundos em repositórios grandes.

7.3. Combinação com sparse checkout

Para máxima eficiência em áreas específicas:

git clone --filter=blob:none --sparse https://github.com/exemplo/monorepo.git
cd monorepo
git sparse-checkout set frontend/src

Você baixa apenas a estrutura de diretórios e, quando fizer checkout, apenas os blobs da pasta frontend/src.

8. Limitações, armadilhas e alternativas

8.1. Dependência de conectividade

Partial clone exige conexão constante com o servidor. Operações como git log -p (que mostra diffs) baixam blobs sob demanda. Sem internet, comandos que precisam de objetos faltantes falham.

8.2. Incompatibilidades

  • Servidores antigos: versões do Git anteriores a 2.19 não suportam partial clone
  • Hooks personalizados: hooks que escaneiam todos os arquivos podem forçar downloads indesejados
  • Ferramentas externas: IDEs e ferramentas de análise podem não entender partial clones e tentar baixar tudo

8.3. Alternativas

  • Shallow clone: melhor quando você nunca precisará do histórico completo
  • Bundle files: útil para transferir repositórios grandes offline
  • Git LFS: específico para arquivos binários grandes, com armazenamento separado

Referências