Submódulos e subárvores do Git
Domine as técnicas para gerenciar dependências externas e projetos com vários módulos usando Git.
Submódulos e subárvores do Git
Quando seus projetos se tornam complexos e dependem de outros repositórios Git, os submódulos e as subárvores se tornam essenciais. Este tutorial vai ensinar você a gerenciar com eficiência projetos com vários repositórios usando Git.
Introdução às dependências no Git
Problemas comuns
Desafios:
- Compartilhar código entre projetos
- Gerenciar as versões das dependências
- Manter a consistência
- Evitar a duplicação
Soluções do Git
- Submódulos (Submodules): referências a outros repositórios
- Subárvores (Subtrees): integração do código externo
- Alternativas: gerenciadores de pacotes, monorepos
Git Submodules
Conceito
Um submódulo é um repositório Git incorporado a outro repositório Git, que mantém seu próprio histórico e suas próprias referências.
projet-principal/
├── src/
├── lib/
│ └── shared-utils/ # <- Submódulo
│ ├── .git # Repositório Git separado
│ └── utils.js
├── .gitmodules # Configuração dos submódulos
└── README.md
Adicionar um submódulo
# Adicionar um submódulo
git submodule add https://github.com/user/shared-utils.git lib/shared-utils
# Verificar a adição
cat .gitmodules
Conteúdo de .gitmodules:
[submodule "lib/shared-utils"]
path = lib/shared-utils
url = https://github.com/user/shared-utils.git
# Fazer o commit da adição do submódulo
git add .gitmodules lib/shared-utils
git commit -m "Add shared-utils submodule"
Clonar um projeto com submódulos
# Método 1: clone com inicialização automática
git clone --recurse-submodules https://github.com/user/projet-principal.git
# Método 2: clone e depois inicialização
git clone https://github.com/user/projet-principal.git
cd projet-principal
git submodule init
git submodule update
# Método 3: comando combinado
git submodule update --init --recursive
Trabalhar com submódulos
Atualizar um submódulo
# Entrar no submódulo
cd lib/shared-utils
# Buscar as últimas alterações
git fetch
git merge origin/main
# Ou diretamente
git pull origin main
# Voltar ao projeto principal
cd ../..
# Fazer o commit da nova referência
git add lib/shared-utils
git commit -m "Update shared-utils to latest version"
Atualizar todos os submódulos
# Atualizar todos os submódulos
git submodule update --remote
# Com merge automático
git submodule update --remote --merge
# Com rebase
git submodule update --remote --rebase
Modificar um submódulo
# Entrar no submódulo
cd lib/shared-utils
# Criar uma branch
git checkout -b feature/new-utility
# Desenvolver
echo "export function newUtil() { return 'new'; }" >> utils.js
git add utils.js
git commit -m "Add new utility function"
# Enviar para o repositório do submódulo
git push origin feature/new-utility
# Voltar ao projeto principal
cd ../..
# O submódulo agora aponta para o novo commit
git add lib/shared-utils
git commit -m "Update shared-utils with new utility"
Configuração avançada
Branches específicas
# Configurar um submódulo para acompanhar uma branch
git config -f .gitmodules submodule.lib/shared-utils.branch develop
# Atualizar para essa branch
git submodule update --remote
URLs relativas
# .gitmodules com URL relativa
[submodule "lib/shared-utils"]
path = lib/shared-utils
url = ../shared-utils.git
Ignorar modificações
# Ignorar as modificações não commitadas no submódulo
git config submodule.lib/shared-utils.ignore dirty
# Ignorar todas as alterações
git config submodule.lib/shared-utils.ignore all
Remover um submódulo
# 1. Desativar o submódulo
git submodule deinit lib/shared-utils
# 2. Remover do repositório
git rm lib/shared-utils
# 3. Remover a pasta .git/modules
rm -rf .git/modules/lib/shared-utils
# 4. Fazer o commit da remoção
git commit -m "Remove shared-utils submodule"
Git Subtrees
Conceito
Uma subárvore integra o código de outro repositório diretamente no seu repositório, sem manter uma referência externa.
projet-principal/
├── src/
├── lib/
│ └── shared-utils/ # <- Código integrado
│ └── utils.js # Sem .git separado
└── README.md
Adicionar uma subárvore
# Adicionar uma subárvore
git subtree add --prefix=lib/shared-utils \
https://github.com/user/shared-utils.git \
main --squash
# Verificar a adição
ls lib/shared-utils/
Atualizar uma subárvore
# Buscar as atualizações
git subtree pull --prefix=lib/shared-utils \
https://github.com/user/shared-utils.git \
main --squash
Contribuir com uma subárvore
# Modificar o código da subárvore
echo "export function newFeature() { return 'feature'; }" >> lib/shared-utils/utils.js
git add lib/shared-utils/utils.js
git commit -m "Add new feature to shared utils"
# Enviar as modificações para o repositório original
git subtree push --prefix=lib/shared-utils \
https://github.com/user/shared-utils.git \
main
Extrair uma subárvore
# Criar um novo repositório a partir de uma subpasta
git subtree split --prefix=lib/shared-utils -b shared-utils-branch
# Criar um novo repositório
mkdir ../shared-utils-extracted
cd ../shared-utils-extracted
git init
git pull ../projet-principal shared-utils-branch
Comparação entre Submodules e Subtrees
Tabela comparativa
| Aspecto | Submodules | Subtrees |
|---|---|---|
| Complexidade | Alta | Média |
| Histórico | Separado | Integrado |
| Tamanho do repositório | Pequeno | Maior |
| Clonagem | Etapas adicionais | Padrão |
| Modificações | Complexas | Simples |
| Versões | Explícitas | Implícitas |
| Colaboração | Difícil | Mais fácil |
Quando usar cada um?
Submodules:
- Dependências estáveis
- Controle rigoroso das versões
- Equipes separadas
- Grandes projetos externos
Subtrees:
- Desenvolvimento ativo
- Colaboração frequente
- Simplicidade como prioridade
- Pequenas bibliotecas
Casos de uso práticos
Projeto com biblioteca compartilhada
# Estrutura do projeto
mkdir monorepo-example
cd monorepo-example
git init
# Criar a biblioteca comum
mkdir -p shared/utils
echo "export function formatDate(date) {
return date.toISOString().split('T')[0];
}" > shared/utils/date.js
git add shared/
git commit -m "Add shared utilities"
# Adicionar como submódulo em outros projetos
cd ../web-app
git submodule add ../monorepo-example/shared shared/utils
Tema WordPress com submódulos
# Tema principal
mkdir wp-theme
cd wp-theme
git init
# Adicionar um framework CSS
git submodule add https://github.com/twbs/bootstrap.git assets/bootstrap
# Adicionar uma biblioteca JS
git submodule add https://github.com/jquery/jquery.git assets/jquery
# Estrutura final
# wp-theme/
# ├── assets/
# │ ├── bootstrap/ # Submódulo
# │ └── jquery/ # Submódulo
# ├── style.css
# └── index.php
Microsserviços com dependências comuns
# Serviço de usuário
mkdir user-service
cd user-service
git init
# Adicionar os utilitários comuns
git subtree add --prefix=lib/common \
https://github.com/company/common-lib.git \
main --squash
# Serviço de pedidos
cd ../order-service
git init
git subtree add --prefix=lib/common \
https://github.com/company/common-lib.git \
main --squash
Exercícios práticos
Exercício 1: Submodules básicos
# 1. Criar um repositório de biblioteca
mkdir math-lib
cd math-lib
git init
echo "export function add(a, b) { return a + b; }
export function multiply(a, b) { return a * b; }" > math.js
git add math.js
git commit -m "Initial math library"
# Simular um repositório remoto
cd ..
git clone --bare math-lib math-lib.git
# 2. Criar o projeto principal
mkdir calculator-app
cd calculator-app
git init
echo "# Calculator App" > README.md
git add README.md
git commit -m "Initial commit"
# 3. Adicionar o submódulo
git submodule add ../math-lib.git lib/math
git commit -m "Add math library submodule"
# 4. Usar a biblioteca
echo "import { add, multiply } from './lib/math/math.js';
console.log('2 + 3 =', add(2, 3));
console.log('4 * 5 =', multiply(4, 5));" > app.js
git add app.js
git commit -m "Add calculator app using math library"
Exercício 2: Subtrees com contribuição
# 1. Criar uma biblioteca de utilitários
mkdir utils-lib
cd utils-lib
git init
echo "export function capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}" > string-utils.js
git add string-utils.js
git commit -m "Add string utilities"
# 2. Criar o projeto principal
cd ..
mkdir blog-app
cd blog-app
git init
# 3. Adicionar como subárvore
git subtree add --prefix=utils ../utils-lib main --squash
# 4. Modificar e contribuir
echo "export function slugify(str) {
return str.toLowerCase().replace(/\s+/g, '-');
}" >> utils/string-utils.js
git add utils/string-utils.js
git commit -m "Add slugify function to string utils"
# 5. Enviar a contribuição
git subtree push --prefix=utils ../utils-lib main
Exercício 3: Gerenciamento de versões com submodules
# 1. No repositório da biblioteca
cd math-lib
git tag v1.0.0
# Adicionar uma nova função
echo "export function subtract(a, b) { return a - b; }" >> math.js
git add math.js
git commit -m "Add subtract function"
git tag v1.1.0
# 2. No projeto principal
cd ../calculator-app
# Atualizar para uma versão específica
cd lib/math
git fetch --tags
git checkout v1.1.0
cd ../..
git add lib/math
git commit -m "Update math library to v1.1.0"
# 3. Usar a nova função
echo "import { subtract } from './lib/math/math.js';
console.log('10 - 3 =', subtract(10, 3));" >> app.js
git add app.js
git commit -m "Use subtract function"
Boas práticas
Para submodules
-
Documentação clara
# README.md ## Installation ```bash git clone --recurse-submodules https://github.com/user/project.gitMise à jour des sous-modules
git submodule update --remote -
Scripts de automação
#!/bin/bash # scripts/update-submodules.sh echo "Updating all submodules..." git submodule update --remote --merge echo "Committing updates..." git add . git commit -m "Update submodules to latest versions" -
Hooks do Git
#!/bin/bash # .git/hooks/post-checkout # Atualizar os submódulos após o checkout git submodule update --init --recursive
Para subtrees
-
Aliases do Git
git config alias.sba 'subtree add --prefix' git config alias.sbu 'subtree pull --prefix' git config alias.sbp 'subtree push --prefix' -
Documentação das fontes
# SUBTREES.md ## lib/shared-utils - Source: https://github.com/company/shared-utils.git - Branch: main - Last update: 2024-01-15 ### Update command: ```bash git subtree pull --prefix=lib/shared-utils \ https://github.com/company/shared-utils.git \ main --squash
Alternativas modernas
Git Worktrees
# Criar árvores de trabalho separadas
git worktree add ../feature-branch feature/new-feature
git worktree add ../hotfix-branch hotfix/critical-fix
# Listar as árvores de trabalho
git worktree list
# Remover uma árvore de trabalho
git worktree remove ../feature-branch
Gerenciadores de pacotes
// package.json avec dépendances Git
{
"dependencies": {
"shared-utils": "git+https://github.com/company/shared-utils.git#v1.2.0"
}
}
Monorepos
# Estrutura de monorepo
project/
├── packages/
│ ├── shared-utils/
│ ├── web-app/
│ └── mobile-app/
├── tools/
└── package.json
Solução de problemas
Problemas comuns com submodules
# Submódulo vazio após o clone
git submodule update --init --recursive
# Conflito de submódulo
git reset --hard HEAD
git submodule update --init --recursive
# Submódulo em estado detached
cd submodule-path
git checkout main
cd ..
git add submodule-path
git commit -m "Fix detached submodule"
Problemas com subtrees
# Erro "Working tree has modifications"
git stash
git subtree pull --prefix=lib/utils origin main --squash
git stash pop
# Histórico poluído
# Usar --squash para evitar o histórico completo
git subtree add --prefix=lib/utils origin main --squash
Recursos adicionais
Ferramentas
- Git Subrepo: alternativa aos submodules
- Lerna: gerenciamento de monorepos JavaScript
- Nx: ferramentas de desenvolvimento para monorepos
- Bazel: sistema de build para monorepos
Documentação
Conclusão
Os submódulos e as subárvores são ferramentas poderosas para gerenciar projetos complexos com dependências externas. A escolha entre os dois depende das suas necessidades específicas em termos de simplicidade, controle e colaboração.
No próximo tutorial, vamos explorar o GitHub Actions para automatizar seus workflows de desenvolvimento e implantação.
Tempo de leitura estimado: 25 minutos
Nível: Avançado
Etapa anterior: Workflows Git avançados
Próxima etapa: GitHub Actions e CI/CD