# Git submodules: o que são, como usar e quando vale a pena

> O que são os submódulos do Git, por que existem, exemplos de uso com comandos, prós e contras, projetos reais no GitHub e um quiz para decidir se vale a pena.

- URL: https://iagofrota.com.br/blog/git-submodules-o-que-sao-e-quando-usar/
- Data: 2026-10-12
- Tags: git, submodules, versionamento, github, engenharia

import SubmodulesQuiz from '../../../components/SubmodulesQuiz.astro';

O GIT é um mundo e a gente usa as funções mais básicas dele no dia a dia (e tá tudo bem!). Mas de vez em quando eu gosto de revisar algo e descobrir o que posso estar melhorando no meu operacional.

Já escrevi aqui no blog sobre outras partes do Git:

1. Em abril de 2020, o [git bare](https://iagofrota.com.br/blog/deploy-utilizando-git-bare/), para automatizar o deploy das minhas aplicações.
2. No mês seguinte, os [estágios do Git](https://iagofrota.com.br/blog/entendendo-os-estagios-do-git/). Lá eu admiti que, por muito tempo, utilizava certos comandos sem entender o que realmente estava acontecendo.
3. Em setembro de 2023, o [`git stash`](https://iagofrota.com.br/blog/o-que-voce-precisa-saber-sobre-o-git-stash/).

Esses dias fui procurar na minha máquina quais repositórios usam submódulos. Achei um arquivo `.gitmodules` em um projeto pessoal e em alguns dos projetos da empresa onde eu trabalho.

Ver o arquivo é fácil. Explicar de cabeça por que ele está ali, nem tanto. Era a curiosidade batendo outra vez.

Por isso fui ler a documentação e rodei os comandos principais abaixo em um repositório de teste, com o Git 2.43, antes de escrever.

Neste artigo eu cubro seis coisas:

1. O que são os submódulos.
2. Por que o Git ganhou esse recurso.
3. Exemplos de uso, com os comandos.
4. Um quiz para você decidir se vale a pena no seu caso.
5. Os prós e os contras.
6. Projetos no GitHub que usam submódulos.

Bora lá.

## O que são submódulos

O [Pro Git](https://git-scm.com/book/en/v2/Git-Tools-Submodules), o livro oficial, define assim: submódulos permitem manter um repositório Git como subdiretório de outro, com os commits separados.

Na prática, o repositório de fora (o _superprojeto_) não guarda os arquivos do de dentro. Ele guarda duas coisas: o endereço, no arquivo `.gitmodules`, e **um commit exato** do repositório de dentro.

```bash
git submodule add https://github.com/exemplo/lib vendor/lib
git status --short
```

```text
A  .gitmodules
A  vendor/lib
```

O `.gitmodules` é versionado junto com o resto, então todo mundo que clonar sabe de onde buscar:

```ini
[submodule "vendor/lib"]
	path = vendor/lib
	url = https://github.com/exemplo/lib
```

E o commit fica registrado como um item especial, de modo `160000`:

```bash
git ls-files -s vendor/lib
```

```text
160000 7ee8d1e49243cb53e518db1bbf8c3af03c082513 0	vendor/lib
```

O Git não guarda os arquivos. Guarda o endereço e o commit.

Repositório dentro de repositório não é, por si só, submódulo. O meu blog, por exemplo, tem uma pasta `iagofrota-blog/` que é um repositório próprio, mas o `.gitignore` do repositório de fora a ignora. Ele nem sabe que ela existe. Submódulo é quando o Git de fora **registra** a relação.

## Por que existem

O Pro Git abre o assunto com um problema comum. Você está construindo um site e quer usar uma biblioteca que já existe. Se pegar pelo gerenciador de pacotes, fica difícil customizar. Se copiar o código para dentro do projeto, suas alterações viram uma dor de cabeça quando a biblioteca lançar versão nova.

O submódulo é a resposta do Git para isso: você clona o outro repositório dentro do seu e mantém os commits de cada um separados.

Quanto à história, o Git ganhou o comando `git submodule` na versão 1.5.3, lançada em setembro de 2007. As [notas dessa versão](https://github.com/git/git/blob/master/Documentation/RelNotes/1.5.3.adoc) dizem que o suporte era mínimo, e que isso era de propósito. Uma das decisões de desenho foi esta: as operações no superprojeto **não entram nos submódulos por padrão**.

Isso explica o comportamento que mais confunde quem chega: você clona o projeto e a pasta do submódulo vem vazia. Não é bug. É decisão de 2007.

## Na prática

### Clonar um projeto com submódulos

Sem nenhuma flag, o clone cria a pasta e deixa ela vazia. No meu teste, `ls` na pasta devolveu zero arquivos, e o `git submodule status` mostrou o hífen que marca um submódulo não inicializado:

```bash
git clone https://github.com/exemplo/app
cd app
git submodule status
```

```text
-7ee8d1e49243cb53e518db1bbf8c3af03c082513 vendor/lib
```

Dois caminhos resolvem. O primeiro é pedir tudo já no clone:

```bash
git clone --recurse-submodules https://github.com/exemplo/app
```

O segundo é corrigir um clone que já existe:

```bash
git submodule update --init --recursive
```

### Atualizar para a versão nova

Para trazer o commit mais novo da branch remota do submódulo (por padrão, a `HEAD` do remoto, ou a branch definida em `branch` no `.gitmodules`):

```bash
git submodule update --remote
git status --short
git diff --submodule
```

```text
 M vendor/lib
Submodule vendor/lib 7ee8d1e..739f438:
  > lib: v2
```

Repare que o `status` marca a pasta como modificada, mas não há diff de arquivo. O que mudou foi o **ponteiro**. Falta commitar o novo commit de referência no superprojeto, e só então o resto do time recebe a atualização.

Outro detalhe: um `git pull` no superprojeto traz o ponteiro novo, mas não atualiza os arquivos do submódulo. Para isso é preciso rodar `git submodule update` de novo. Quem se cansa disso pode ligar a configuração `submodule.recurse`:

```bash
git config --global submodule.recurse true
```

### Mexer dentro do submódulo

Depois do `update`, o submódulo fica em _detached HEAD_, sem branch. É o comportamento padrão do checkout; com `--merge` ou `--rebase` ele é outro. No meu teste:

```text
HEAD detached at 739f438
```

Se você commitar assim, o commit fica sem nenhuma branch apontando para ele, e depois é difícil de achar. Entre na pasta e crie uma branch a partir do commit atual:

```bash
cd vendor/lib
git switch -c minha-alteracao
# ...edite, commite...
cd ../..
```

Prefira criar a branch a dar um `git checkout main`. A `main` local pode estar num commit mais antigo, e o checkout devolveria o submódulo para ele, desfazendo a atualização que você acabou de fazer.

### Publicar na ordem certa

Aqui mora o erro mais caro. Se você commitar no submódulo, atualizar o ponteiro no superprojeto e publicar só o superprojeto, o ponteiro aponta para um commit que **só existe na sua máquina**. Quem clonar depois vai tropeçar.

O Git avisa, se você pedir. Com a opção `check`, o push falha, e a mensagem do meu teste foi esta:

```bash
git push --recurse-submodules=check origin main
```

```text
The following submodule paths contain changes that can
not be found on any remote:
  vendor/lib

Please try

	git push --recurse-submodules=on-demand
```

O `check` só confirma que o commit existe em algum remoto do submódulo. Não garante que seja o remoto dos seus colegas, nem que eles tenham acesso a ele.

Com `on-demand`, o Git publica o submódulo primeiro e o superprojeto depois. Se você quer isso sempre, configure uma vez:

```bash
git config push.recurseSubmodules on-demand
```

### Remover um submódulo

Remover dá mais trabalho do que adicionar. O caminho seguro, no meu teste, foram dois comandos:

```bash
git rm vendor/lib
git commit -m "remove o submódulo vendor/lib"
```

O `git rm` tira a pasta e a entrada do `.gitmodules`. Se o submódulo tiver alteração local, ele recusa e avisa. Resolva o trabalho antes, em vez de forçar com `-f`: o `-f` descarta o que não foi commitado.

Sobra uma cópia do repositório do submódulo em `.git/modules/vendor/lib`. O Git deixa isso de propósito, para você conseguir voltar a versões antigas do projeto. Só apague com `rm -rf` se tiver certeza de que não há commit seu guardado apenas ali, porque esse `rm -rf` não tem volta.

### No CI

Pipeline também clona. O `actions/checkout`, do GitHub Actions, aceita a opção [`submodules`](https://github.com/actions/checkout), com `true` ou `recursive`. Sem ela, o job roda com a pasta vazia e a falha aparece longe da causa.

## Quando usar

Antes do quiz, a regra em quatro linhas:

1. Se o código já está publicado em um gerenciador de pacotes (npm, Maven Central, PyPI, Packagist) e a versão publicada atende, use o gerenciador. Ele já fixa a versão para você.
2. Se nada exige os repositórios separados (outro time, outro ciclo de release, outro controle de acesso), considere juntar em um só.
3. Se é código de terceiro que você dificilmente vai devolver, e o clone precisa ser simples, olhe o `git subtree`.
4. Se você precisa de um commit exato de outro repositório, com histórico separado, submódulo.

A [Opensource.com](https://opensource.com/article/20/5/git-submodules-subtrees) separa os dois assim: submódulo para um repositório externo que é seu e para o qual você vai empurrar mudanças, subtree para código de terceiro que você provavelmente não vai devolver.

A regra tem uma zona cinzenta, e vale ser honesto: o PyTorch, que você vai ver mais abaixo, fixa dezenas de projetos de terceiros como submódulos. O `.gitmodules` mostra a fixação, e não o motivo. Minha hipótese é o commit exato, mas é só hipótese.

### Teste com o seu caso

Montei as mesmas perguntas em um quiz de sim ou não. Responda na ordem. O veredito aparece assim que houver resposta suficiente para decidir.

<SubmodulesQuiz />

## Prós e contras

Os prós:

1. **Commit exato.** O superprojeto aponta para uma versão específica do código, o que ajuda a reproduzir o build.
2. **Repositório pequeno.** Ele guarda só o ponteiro, e não o código nem o histórico do outro projeto.
3. **Histórico separado.** Cada repositório mantém os próprios commits, releases e permissões.
4. **Devolver mudanças é simples.** Você entra na pasta, commita e publica no repositório de origem. Num subtree isso dá mais trabalho.

Os contras:

1. **O clone ganha um passo.** Sem `--recurse-submodules`, a pasta vem vazia.
2. **Detached HEAD.** Quem não sabe disso deixa commits sem branch e depois não consegue achá-los.
3. **É fácil esquecer de publicar o submódulo.** O ponteiro vai para o servidor e o commit, não.
4. **Depende de um servidor acessível.** O repositório de origem precisa estar no ar, e se o commit referenciado sumir de lá, o checkout quebra.
5. **Conflito de ponteiro.** Quando duas pessoas movem o mesmo submódulo para commits que divergem, em branches diferentes, o merge pede que você resolva na mão.
6. **Curva de aprendizado.** O artigo da Opensource.com diz que iniciantes costumam achar submódulos difíceis.
7. **Remover dá trabalho**, e o `-f` pode custar trabalho não commitado, como vimos acima.

## Projetos no GitHub

Conferi o `.gitmodules` de cada um em 9 de outubro de 2026. Os números mudam com o tempo.

1. **[PyTorch](https://github.com/pytorch/pytorch).** Tinha 36 entradas, quase todas em `third_party/`: googletest, pybind11, benchmark e outras.
2. **[gRPC](https://github.com/grpc/grpc).** Tinha 20, também em `third_party/`: abseil-cpp, protobuf, re2, zlib e googletest, entre outras.
3. **[Prezto](https://github.com/sorin-ionescu/prezto).** Um framework de configuração para Zsh. Tinha 10 entradas, em `modules/*/external`: temas e plugins de terceiros como powerlevel10k, pure e agnoster.
4. **[Hugo](https://gohugo.io/getting-started/quick-start/).** O quick start manda adicionar o tema Ananke como submódulo, em `themes/ananke`. O repositório do Hugo em si não usa.
5. **Um projeto na minha máquina.** O `meeting-minutes` aponta `backend/whisper.cpp` para um fork do whisper.cpp, na branch `develop`.

Na minha leitura, o padrão se repete: bibliotecas C++, onde não há um gerenciador de pacotes dominante, e temas ou plugins de terceiros que você quer atualizar no seu ritmo.

## Conclusão

Por fim, submódulo é uma ferramenta de **commit exato**, e não de conveniência. Quem escolhe por achar que é "o jeito do Git de importar biblioteca" costuma sentir o atrito no primeiro clone.

No fim do dia, a pergunta é quanto atrito o seu time aguenta em troca de ter um commit fixado e um histórico separado. Se a resposta for "pouco", o gerenciador de pacotes ou o subtree provavelmente servem melhor.

Escrever sobre git bare, estágios, stash e agora submódulos me lembrou de uma coisa: o Git que eu uso todo dia é uma parte pequena do Git que existe. (Sei que ainda tem muita função que eu não conheço.) É por isso que eu continuo curioso.

E você, qual foi a última função do Git que fez você abrir a documentação só por curiosidade?

## Referências

1. [Pro Git: Git Tools - Submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules)
2. [Git 1.5.3 Release Notes](https://github.com/git/git/blob/master/Documentation/RelNotes/1.5.3.adoc)
3. [Managing Git projects with submodules and subtrees (Opensource.com)](https://opensource.com/article/20/5/git-submodules-subtrees)
4. [actions/checkout](https://github.com/actions/checkout)
5. [Hugo Quick Start](https://gohugo.io/getting-started/quick-start/)
