0%
Tutorial 9 de 10
90% concluído

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

Projet PrincipalBibliothèque CommuneModule UIAPI ClientUtilitaires

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

  1. Submódulos (Submodules): referências a outros repositórios
  2. Subárvores (Subtrees): integração do código externo
  3. 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

AspectoSubmodulesSubtrees
ComplexidadeAltaMédia
HistóricoSeparadoIntegrado
Tamanho do repositórioPequenoMaior
ClonagemEtapas adicionaisPadrão
ModificaçõesComplexasSimples
VersõesExplícitasImplícitas
ColaboraçãoDifícilMais 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

  1. Documentação clara

    # README.md
    ## Installation
    
    ```bash
    git clone --recurse-submodules https://github.com/user/project.git

    Mise à jour des sous-modules

    git submodule update --remote
  2. 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"
  3. Hooks do Git

    #!/bin/bash
    # .git/hooks/post-checkout
    
    # Atualizar os submódulos após o checkout
    git submodule update --init --recursive

Para subtrees

  1. Aliases do Git

    git config alias.sba 'subtree add --prefix'
    git config alias.sbu 'subtree pull --prefix'
    git config alias.sbp 'subtree push --prefix'
  2. 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