Voltar para todos os artigos
Git hardcore: hooks, submodules, monorepos e sobrevivência em códigos gigantes

Git hardcore: hooks, submodules, monorepos e sobrevivência em códigos gigantes

Técnicas avançadas de Git para códigos grandes: hooks, submodules, monorepos, performance e plano de desastre.

Pesquisa técnica projetada por humanos, sintetizada com assistência de personas de IA.
7 min de leitura

TL;DR / Sumário Executivo

Técnicas avançadas de Git para códigos grandes: hooks, submodules, monorepos, performance e plano de desastre.

💡 TL;DR (Resumo)

Repositórios grandes pedem ferramentas avançadas. git hooks automatiza checagens locais. git submodules é mais problema que solução (evite). Monorepos funcionam se bem estruturados. Performance melhora com .gitignore, Git LFS e clones shallow. Trate o repo como infra crítica: backups, branch protegida, plano de desastre. A verdade: no final, a maioria dos problemas "de Git" é na verdade problema de organização.

Até aqui a gente falou de Git no dia a dia. Agora é hora de encarar as partes que a galera finge que não existe porque dá trabalho:

  • Hooks de Git para automatizar checagens
  • Submodules (sim, aquele bicho estranho)
  • Monorepos e estratégias para repositórios gigantes
  • Performance: quando o Git come sua RAM e CPU
  • Plano de desastre para não virar refém de um único repositório

Esse é o artigo para quando você já sabe usar Git, mas o tamanho do código começou a doer.


1. Hooks de Git: automatizando boas práticas

Hooks são scripts que o Git executa automaticamente em certos momentos:

  • Antes de um commit (pre-commit)
  • Depois de um commit (post-commit)
  • Antes de um push (pre-push)
  • Etc.

Eles moram em .git/hooks/.

1.1. Habilitando um pre-commit simples

Exemplo: impedir commit se existirem console.log perdidos.

Crie o arquivo .git/hooks/pre-commit:

bash
#!/usr/bin/env bash if git diff --cached | grep -q "console.log"; then echo " [pre-commit] Encontrado 'console.log' no diff. Remova antes de commitar." >&2 exit 1 fi

Dê permissão de execução:

bash
chmod +x .git/hooks/pre-commit

Agora, se você tentar commitar com console.log, o commit falha.

1.2. Rodando linter/testes automaticamente

bash
#!/usr/bin/env bash npm test STATUS=$? if [ $STATUS -ne 0 ]; then echo " [pre-commit] Testes falharam. Arrume antes de commitar." >&2 exit $STATUS fi

Controvérsia: hook só local vira gambiarra se o time todo não usar. Para times grandes, vale padronizar via ferramentas tipo Husky (JS), pre-commit (Python) ou via CI.


2. Submodules: quando (quase nunca) usar

Submodule é basicamente um repositório Git dentro de outro.

Uso clássico:

  • Você tem uma lib compartilhada entre vários projetos
  • Quer manter o histórico dessa lib separado, mas versionado junto

2.1. Adicionando um submodule

bash
git submodule add https://github.com/org/lib-compartilhada.git libs/lib-compartilhada git commit -m "Adiciona submodule da lib compartilhada"

Isso cria uma entrada no .gitmodules e trata libs/lib-compartilhada como submodule.

2.2. Clonando repo com submodules

bash
git clone https://github.com/org/projeto-com-submodules.git cd projeto-com-submodules git submodule update --init --recursive

Sem isso, os diretórios de submodule ficam vazios.

2.3. Por que muita gente odeia submodules

  • Fluxo de uso é mais chato
  • Fácil quebrar se o time não souber mexer
  • CI precisa ser configurado com carinho

Alternativas modernas:

  • Repos separados com gestão de versão (npm, pip, maven, etc.)
  • Monorepo com gerenciador de workspaces (Nx, Turborepo, pnpm, Bazel, etc.)

Opinião polêmica: submodule não é "errado". Errado é enfiar submodule em time que mal sabe fazer merge.


3. Monorepos: um repo para governar todos

Monorepo é quando você coloca vários projetos (serviços, libs, apps) no mesmo repositório Git.

3.1. Vantagens

  • Mais fácil manter versões compatíveis
  • Refactors entre serviços ficam mais controlados
  • CI pode ser otimizado por paths
  • Menos "onde vive esse código?" na sua vida

3.2. Desvantagens

  • Repo pode ficar gigante
  • Clone inicial demorado
  • Histórico muito ruidoso se não organizar bem

3.3. Estrutura típica de monorepo

text
.
├── apps
│   ├── web
│   ├── mobile
│   └── admin
├── services
│   ├── users
│   ├── billing
│   └── notifications
├── libs
│   ├── ui
│   ├── core
│   └── utils
└── tools
    └── ci-scripts

3.4. Comandos Git úteis em monorepo

Histórico de um subdiretório apenas:

bash
# ver histórico só do serviço de billing git log --oneline -- services/billing

Diferença entre branches apenas para um diretório:

bash
git diff main..minha-branch -- services/billing

Blame focado numa parte do repo:

bash
git blame services/billing/src/invoice_service.ts

3.5. Filtrando histórico por path

Útil para gerar um histórico "por projeto" dentro do monorepo:

bash
git log --oneline -- services/users

Ou mesmo extrair um subdiretório para outro repositório mantendo histórico (com git filter-repo ou filter-branch).


4. Performance: quando o Git come sua máquina

Repositórios gigantes podem deixar o Git lento:

  • Muitos arquivos binários
  • Histórico gigantesco de merges
  • Diretórios node_modules ou dist commitados sem querer

4.1. Use .gitignore direito

Não tem conversa: se você está commitando build, dependências e lixo, vai sofrer.

Exemplo básico para projetos JS:

gitignore
node_modules/ dist/ coverage/ .env *.log

4.2. Git LFS para arquivos grandes

Arquivos binários gigantes (imagens pesadas, vídeos, modelos de ML) não se dão bem com Git puro.

Use Git LFS (Large File Storage):

bash
git lfs track "*.psd" echo "*.psd filter=lfs diff=lfs merge=lfs -text" >> .gitattributes

4.3. Clones rasos (--depth)

Em CI ou ambientes onde você não precisa do histórico inteiro:

bash
git clone --depth=50 https://github.com/org/projeto-gigante.git

Isso baixa só os últimos 50 commits.

4.4. Limpeza periódica

Com o tempo, repositórios podem acumular refs, tags antigas e branches mortas.

bash
# remove refs remotas deletadas git fetch --prune # limpa objetos soltos (local) git gc --prune=now --aggressive

Dica: não rode git gc --aggressive todo dia, mas de vez em quando em repositórios grandes pode ajudar.


5. Plano de desastre para repositórios críticos

Se o seu repositório é o coração do produto, trate ele como tal.

5.1. Backups e remotos múltiplos

Você pode ter mais de um remoto configurado:

bash
git remote add backup git@backup-server:org/projeto.git git push --all backup git push --tags backup

Se o GitHub/GitLab da vida tiver um dia ruim, você tem cópia.

5.2. Proteção de branch

Em provedores tipo GitHub/GitLab/Bitbucket:

  • Proteja main/master contra push --force
  • Exija PR + review para merges
  • Exija status de CI verde

Isso não é burocracia: é o que impede git push --force errado sexta às 18h.

5.3. Recuperação após desastre

Se alguém fizer besteira muito grande:

  1. Pare tudo (nada de push até entender a situação)
  2. Use git reflog nos clones locais que ainda estão saudáveis
  3. Crie uma nova branch a partir de um commit bom
  4. Combine com o time como migrar para esse ponto seguro

Ter clones atualizados em máquinas diferentes já é uma forma de backup implícito.


6. Resumo hardcore

Quando o repositório fica grande e a bagunça cresce:

  • Use hooks para automatizar checks locais
  • Evite submodules a não ser que o time saiba muito bem o que está fazendo
  • Considere monorepo se você tem muitos serviços interligados
  • Cuide de performance: .gitignore, Git LFS, --depth, git gc
  • Trate o repo como infra crítica: backup, branch protegida, plano de desastre

Se esse artigo fez sentido, provavelmente você já tomou umas pancadas de Git na vida.

Manda praquele amigo que ainda acha que git é só "salvar código no GitHub".

Me encontra no X/Twitter: @gss_62

#git #gittips

Receba novos artigos

Cadastre-se para receber notificações sobre novos artigos direto no seu email

Não enviaremos spam. Você pode cancelar a inscrição a qualquer momento.