O despertar da lentidão

Você está há um tempo trabalhando no seu projeto de data science. Tem uns vinte notebooks, algumas imagens, e aquela estrutura típica de pastas que parecia uma boa ideia há três meses.

Você roda git status para ver o que mexeu e… espera. E espera. E enquanto espera, dá tempo de se perguntar se o computador travou ou se está apenas meditando.

Spoiler: não está meditando. Está sofrendo.

O problema tem nome (e sobrenome)

Git não é lento. Seu repositório que é.

Quando você executa git status, o git precisa fazer duas coisas que parecem simples mas não são:

  1. Escanear toda a árvore de arquivos para ver o que mudou
  2. Comparar cada arquivo com o que tem salvo

Num repositório normal isso é instantâneo. Mas os notebooks do Jupyter são JSON disfarçado de documento. E não qualquer JSON: um que inclui código, saídas, imagens codificadas em base64, metadados do kernel, e basicamente tudo que o cara que projetou o formato pensou em enfiar.

Um notebook “pequeno” com alguns gráficos pode pesar vários megas. Multiplique isso por vinte notebooks e você tem um repositório que geme toda vez que você olha para ele.

E se ainda por cima você renomeia uma pasta… o git interpreta como “você deletou 50 arquivos e criou 50 novos”. Diversão garantida.

Solução 1: Coloque um porteiro no seu repositório

A primeira solução é tão elegante que dá raiva não ter conhecido antes.

Se chama FSMonitor e funciona assim: em vez do git escanear todo o repositório toda vez que você faz algo, o sistema operacional avisa quais arquivos mudaram.

Em outras palavras: é como ter um porteiro na entrada que te fala “só o João entrou” em vez de ter que revisar toda a lista de convidados toda vez.

Para ativar:

git config core.fsmonitor true
git config core.untrackedcache true

Pronto. É isso.

A primeira vez que você fizer git status depois de ativar vai demorar o mesmo tempo (ou mais, porque precisa inicializar o cache). Mas a partir da segunda… mágica.

No meu repositório com mais de 400 arquivos, o git status passou de 2-3 segundos para ser instantâneo. Não “rápido”. Instantâneo.

Funciona em todo lugar?

  • macOS: Sim, usa FSEvents
  • Linux: Sim, usa inotify (se seu kernel suporta, e suporta)
  • Windows: Sim, usa ReadDirectoryChangesW

Ou seja, sim.

Solução 2: Guarde a receita, não a foto do prato

FSMonitor acelera o escaneamento, mas tem outro problema: os notebooks continuam enormes. E toda vez que você executa uma célula e salva, mesmo que o código seja o mesmo, o arquivo muda porque as saídas são diferentes.

Isso significa:

  • Diffs ilegíveis (quem quer revisar base64 de uma imagem?)
  • Commits inflados
  • Merges do inferno

A solução se chama nbstripout e faz exatamente o que o nome indica: remove o output dos notebooks antes de fazer commit.

É como guardar a receita sem as fotos do prato pronto. O código está lá, os resultados você regenera quando quiser.

Para instalar com uv:

uv add nbstripout
uv run nbstripout --install

Ou com pip, se você é old school:

pip install nbstripout
nbstripout --install

O --install configura o git automaticamente para usar o nbstripout como filtro. A partir desse momento, quando você fizer commit de um notebook, ele é salvo sem outputs.

Para verificar que está funcionando:

git config --get filter.nbstripout.clean

Se retornar algo como nbstripout, você está no caminho certo.

E se eu precisar guardar os outputs?

Boa pergunta. Às vezes você quer fazer commit de um notebook já executado, por exemplo para documentação ou para alguém ver sem ter que executar.

O nbstripout permite marcar notebooks específicos para pular o filtro:

git -c diff.nbstripout.textconv=cat diff

Ou você pode configurar exceções no seu .gitattributes. Mas em geral, meu conselho é: não guarde outputs. Notebooks são código, não documentos finais.

Menção honrosa: git-lfs

Se além de notebooks você tem imagens ou vídeos que mudam frequentemente, existe o git-lfs (Large File Storage).

A ideia é simples: em vez de guardar o binário grande no repositório, você guarda um ponteiro e o arquivo real fica num servidor separado.

É como guardar as malas no guarda-volumes do aeroporto e levar só o comprovante.

git lfs install
git lfs track "*.png"
git lfs track "*.mp4"

Por que é só menção honrosa e não solução principal? Porque adiciona complexidade. Você precisa de um servidor LFS (GitHub e GitLab oferecem, mas têm limites), e se alguém clona o repositório sem git-lfs instalado, leva os ponteiros em vez dos arquivos.

Para notebooks, FSMonitor + nbstripout geralmente são suficientes. git-lfs é para quando você tem datasets de verdade ou assets multimídia.

O combo vencedor

Minha configuração para qualquer repositório com notebooks:

# Velocidade
git config core.fsmonitor true
git config core.untrackedcache true

# Notebooks limpos
uv add nbstripout
uv run nbstripout --install

Quatro comandos e seu repositório passa de vovô com artrite para atleta olímpico.

Verifique que tudo funciona

# FSMonitor ativo
git config --get core.fsmonitor
# Deve retornar: true

# nbstripout configurado
git config --get filter.nbstripout.clean
# Deve retornar: nbstripout

Se ambos retornarem o esperado, pronto. Aproveite seu git status instantâneo e seus diffs legíveis.

Fechamento

Na próxima vez que alguém te disser “git é lento”, você já sabe o que responder: git não é lento, seu repositório está mal configurado.

FSMonitor para que o sistema operacional faça o trabalho pesado. nbstripout para que os notebooks não pesem como se carregassem lastro.

E se depois de tudo isso ainda estiver lento… talvez seja hora de se questionar se você realmente precisa de 200 notebooks no mesmo repositório. Mas essa é outra história.

Este artigo foi escrito originalmente em espanhol e traduzido com a ajuda de IA.