Git Submodule
Introdução aos submódulos do Git: como adicionar, clonar, atualizar, enviar e remover submódulos, com exemplos práticos.
Um submódulo permite incorporar um repositório Git dentro de outro e manter seus históricos completamente separados. Esta página explica o que é um submódulo, quando ele é a ferramenta certa e o ciclo de vida completo dos comandos: adicionar, clonar, fazer pull, atualizar, fazer push e remover um submódulo, além das armadilhas comuns que tornam os submódulos complicados.
O que é um Submódulo
Com muita frequência, um repositório de código depende de código externo proveniente de outros repositórios. Você pode copiar e colar o código externo diretamente no repositório principal, ou usar o sistema de gerenciamento de pacotes de uma linguagem. No entanto, ambos os métodos têm a desvantagem de não rastrear as alterações no repositório externo.
O Git permite incluir outros repositórios Git — chamados de submódulos — dentro de um único repositório. Um submódulo reside em um caminho específico no diretório de trabalho do repositório pai e é, em si, um clone completo de outro repositório com seu próprio histórico .git.
A ideia central é que um submódulo é fixado a um commit exato, não a um branch ou uma tag. O repositório pai armazena apenas o caminho do submódulo, a URL e o SHA do commit esperado. É isso que permite depender de código externo em um ponto conhecido e reproduzível no tempo.
Dois arquivos rastreiam esse relacionamento:
.gitmodules— um arquivo rastreado na raiz do repositório pai. Ele mapeia o caminho de cada submódulo para sua URL remota (e opcionalmente um branch). Esse arquivo é commitado e compartilhado com todos..git/confige a entrada gitlink na árvore — contabilidade local por clone que registra o commit efetivamente verificado (o gitlink aparece com o modo160000).
Os submódulos suportam adição, sincronização, atualização e clonagem, mas como o pai lembra apenas um SHA de commit, atualizar um submódulo é sempre um ato deliberado em duas etapas — nunca automático.
Quando Usar Submódulos
Trabalhar com submódulos é complicado, por isso sugerimos alguns casos de uso mais adequados para eles.
- Se o subprojeto estiver mudando muito rápido ou as próximas mudanças quebrarem a API, fixe o código em um commit específico por segurança.
- Se um componente não for atualizado com frequência e você quiser rastreá-lo como uma dependência de fornecedor.
- Se você representa uma parte do projeto para um terceiro e deseja integrar o trabalho deles em um momento específico (funciona apenas quando as atualizações não são muito frequentes).
- Se o contexto tecnológico permite empacotamento e gerenciamento formal de dependências, você deve usar gerenciadores de pacotes em vez de submódulos.
- Se o seu código-base é enorme e você não quer baixá-lo inteiro toda vez, use submódulos para que os colaboradores baixem apenas as partes de que precisam.
Adicionando um Submódulo
Primeiro, crie (ou acesse) o repositório que vai conter o submódulo:
mkdir git-submodule-demo
cd git-submodule-demo/
git initInitialized empty Git repository in /Users/example/git-submodule-demo/.git/Adicione um submódulo com git submodule add, passando a URL do repositório que você deseja incorporar:
git submodule add https://somehost/example/textexampleCloning into '/Users/example/git-submodule-demo/textexample'...
remote: Counting objects: 8, done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 8 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (8/8), done.O Git imediatamente clona o repositório textexample em uma pasta com o mesmo nome e cria um arquivo .gitmodules. Para colocar o submódulo em um caminho diferente, adicione-o como último argumento, por exemplo git submodule add <url> vendor/textexample.
Agora verifique o estado do repositório com git status:
git statusOn branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: .gitmodules
new file: textexampleObserve que textexample está staged como uma única entrada, não como os arquivos individuais dentro dele. O repositório pai rastreia apenas o ponteiro de commit do submódulo. Faça commit de ambos os arquivos com git add e git commit:
git add .gitmodules textexample
git commit -m "Add textexample submodule"[master (root-commit) d5002d0] Add textexample submodule
2 files changed, 4 insertions(+)
create mode 100644 .gitmodules
create mode 160000 textexampleO modo 160000 é especial: ele marca textexample como um gitlink (um ponteiro para um commit) em vez de um diretório comum.
Verificando o Status do Submódulo
Antes de fazer qualquer alteração, veja a situação atual de cada submódulo:
git submodule status a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0 textexample (v1.2.0)O caractere inicial é significativo: um espaço indica que o submódulo está no commit esperado, um + indica que está verificado em um commit diferente do que o pai registra, e um - indica que ainda não foi inicializado.
Atualizando Submódulos
Os membros da equipe precisam atualizar o código do submódulo quando ele tiver sido modificado em outro lugar. Você não pode depender somente do git pull, pois fazer pull do repositório pai apenas altera o commit registrado do submódulo — ele não toca os arquivos verificados dentro do submódulo. Para verificar o commit que o pai espera, execute:
git submodule updateSem a flag --remote, esse comando verifica o commit registrado no repositório pai e não busca novas alterações upstream.
Para em vez disso fazer pull do commit mais recente do branch rastreado do submódulo (main por padrão, ou o branch definido em .gitmodules), use --remote:
git submodule update --remote textexampleIsso busca o upstream do submódulo e o avança. O repositório pai agora vê um novo ponteiro de commit, portanto você deve fazer git add e commit do submódulo para registrar a alteração.
Para executar uma atualização em todos os submódulos, incluindo os aninhados, adicione --init --recursive:
git submodule update --init --recursiveSe o arquivo .gitmodules for alterado (por exemplo, a URL do submódulo mudar), execute git submodule sync para copiar as novas URLs para o seu .git/config local antes de atualizar:
git submodule sync --recursiveClonando Git Submodules
Para clonar um projeto com submódulos, use o comando git clone. Por padrão, ele clona o repositório pai, mas deixa os diretórios dos submódulos vazios. Você precisa então executar git submodule init e git submodule update. O primeiro atualiza o .git/config local com os mapeamentos de .gitmodules, enquanto o segundo busca os dados do submódulo e verifica o commit registrado.
Se você clonou sem --recurse-submodules, preencha os submódulos depois:
git clone /url/to/repo/with/submodules
git submodule init
git submodule updateO atalho git submodule update --init combina esses dois últimos comandos. Melhor ainda, clone tudo em uma única etapa com --recurse-submodules:
git clone --recurse-submodules /url/to/repo/with/submodulesIsso clona o repositório pai e automaticamente inicializa e verifica cada submódulo, de modo que a árvore de trabalho fica completa imediatamente.
Fazendo Pull do Código do Submódulo
Quando você faz pull de um repositório pai que recebeu novos submódulos, esses submódulos chegam não inicializados. Primeiro, busque o estado mais recente do pai:
git pullSe novos submódulos forem listados, inicialize-os e baixe-os em uma única etapa:
git submodule update --init --recursivegit submodule init por si só apenas copia os mapeamentos para o .git/config local; ele não baixa nenhum código. A etapa update é o que realmente busca o submódulo e verifica o commit registrado. Você pode fazer com que git pull faça isso automaticamente definindo:
git config submodule.recurse trueEnviando Atualizações em um Submódulo
Um submódulo é um repositório Git completo e independente, portanto você faz commit e push dentro do seu diretório exatamente como faria em qualquer outro lugar:
cd textexample
git checkout main
# ...edit files...
git commit -am "Fix typo in textexample"
git push
cd ..Após fazer commit dentro do submódulo, o repositório pai ainda aponta para o commit antigo. Executar git status no pai agora exibe:
modified: textexample (new commits)Registre o novo ponteiro fazendo staging e commit do caminho do submódulo no pai, e então faça push:
git add textexample
git commit -m "Bump textexample to latest"
git pushPara evitar fazer push do pai antes que os commits do submódulo existam no remoto (o que deixaria os colegas com um ponteiro quebrado e inacessível), envie tudo junto:
git push --recurse-submodules=on-demandIsso faz push de quaisquer commits não enviados do submódulo primeiro, depois o pai.
Removendo um Submódulo
Excluir a pasta manualmente não é suficiente — o Git mantém sua contabilidade interna. Remova um submódulo corretamente com:
git submodule deinit -f textexample
git rm textexample
git commit -m "Remove textexample submodule"deinit cancela o registro do submódulo e remove sua árvore de trabalho, git rm exclui o gitlink e sua entrada em .gitmodules, e o commit registra a remoção.
Resumo
Os submódulos são uma boa forma de manter projetos em repositórios separados enquanto ainda os referencia como pastas no diretório de trabalho de outro repositório. O modelo mental a manter é simples: o pai armazena um ponteiro de commit, toda atualização é deliberada, e --recurse-submodules salva você de clones parcialmente preenchidos. Para dependências que mudam com frequência, um gerenciador de pacotes real geralmente é a melhor escolha. Para se aprofundar em fluxos de trabalho relacionados, consulte git clone, git pull e git status.