Submodulos no git
Submódulos no git #
ou: usando repositórios dentro de repositórios.
O git é uma ferramenta fantástica. O que começou como um simples controlador de versões de código evoluiu para algo cada vez mais poderoso.
É usado por diversos tipos de profissionais, foi implementado em diversas soluções que disseminaram seu uso (como o GitHub, BitBucket, ferramentas de integração contínua) e redefiniu como compartilhamos conhecimento de programação.
Hoje, com o git e as ferramentas e soluções que podemos usar com ele, temos segurança para criar, errar, apender e compartilhar novas soluções a partir de soluções anteriores.
Submódulos #
Uma das possibilidades mais interessantes do git é permitir que se faça uma cópia (ou clone) de um repositório dentro de outro repositório já existente. Pode ser usado para várias coisas, como possibilitar o compartilhamento de bibliotecas, módulos, configurações ou quaisquer outros arquivos entre diferentes projetos.
Um exemplo: compartilhando código usando bibliotecas #
Aqui vou criar uma aplicação com uma biblioteca, extrair essa biblioteca para um repositório separado, importar esse repositório como submódulo, e depois ainda mostrar como trabalhar com esse módulo em mais de um projeto.
Vou usar Python nesse exemplo, e o motivo é que eu uso muito Python no dia a dia, e já ouvi muitas vezes a questão “como compartilhar minhas funções do projeto Python com outras pessoas da equipe?”. Mas o conceito de submódulos funciona com qualquer linguagem de programação.
A primeira versão da aplicação está aqui: https://github.com/ehriq/gitsubm_app/tree/p1. Para quem não conhece Python, criei uma biblioteca[1] com o arquivo __init__.py (que identifica que dentro desse diretório há uma biblioteca), e o arquivo numeros.py que armazena as funções importadas. No arquivo app.py apenas importo as funções usadas e exibo os resultados.
Agora, e se eu quiser separar essa biblioteca de forma a permitir que outras pessoas usem e trabalhem nela? O mais fácil é apenas copiar os arquivos ou as funções. Mas isso é pouco prático, porque não permite atualizações automáticas, controle de versão da biblioteca, e com muitas pessoas envolvidas em um projeto, a duplicação de código vai gerar, em pouco tempo, uma redundância do trabalho.
Primeiro passo: isolar a biblioteca em um novo repositório #
Vou criar um novo repositório (pode ser no GitHub, BitBucket, onde quiser), e copiar para lá o código da biblioteca.
Depois de criar o repositório e colocar lá os arquivos, o nosso repositório da biblioteca vai ficar com essa estrutura: https://github.com/ehriq/gitsubm_lib/tree/p2. Perceba que é o mesmo conteúdo que está no diretório analisador da aplicação.
Segundo passo: importar a biblioteca como um submódulo #
Agora que estamos com a biblioteca segregada, podemos “importar” o repositório dentro do nosso repositório da aplicação. Com isso, a biblioteca vai se tornar um repositório interno dentro do repositório principal, e esse repositório interno é chamado de submódulo.
Para fazer isso funcionar, vamos apagar o diretório analisador com o comando abaixo, que apaga todas as referências existentes a este diretório:
git rm --fr --cached analisador/
e depois rodar o seguinte comando:
git submodule add git@github.com:ehriq/gitsubm_lib.git analisador
Nesse momento, rodamos o comando add do git submodule, adicionando o repositório git@github.com:ehriq/gitsubm_lib.git ao nosso projeto, usando o nome analisador.
Quando termina de rodar o comando, temos alguns novos arquivos, que podemos ver com o comando git status:
On branch main
Your branch is up to date with 'origin/main'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: .gitmodules
new file: analisador
O primeiro arquivo é o .gitmodules, que controla os módulos que existem neste repositório, e o outro é o analisador, que é a nossa biblioteca.
Podemos fazer o commit dessas alterações e, ao rodar nossa aplicação, vemos que tudo funciona como esperado. Podemos ver a nova versão do código aqui: https://github.com/ehriq/gitsubm_app/tree/p2.
É importante notar uma coisa: o repositório principal indica o submódulo, mas apontando para um commit específico! Porque no momento de adicionar o submódulo, ele faz um clone do repositório da biblioteca naquele momento, e não mantém automaticamente atualizado.
Terceiro passo: compartilhando sua biblioteca #
Seguindo o que foi feito no segundo passo, é só adicionar o submódulo a outros projetos, usando o git submodule add <repositorio> <diretorio> , e começar a usar.
Mas, e se a biblioteca for atualizada? E como eu posso atualizar a biblioteca?
Atualizando código #
De nada adianta ter uma biblioteca que não pode ser atualizada. Então, vamos ver como isso pode ser feito; mas antes, precisamos entender como o submódulo se comporta dentro do repositório.
Se rodamos o comando git remote -v dentro do repositório da aplicação, vemos a resposta esperada:
origin git@github.com:ehriq/gitsubm_app.git (fetch)
origin git@github.com:ehriq/gitsubm_app.git (push)
Mas dentro do diretório analisador, ao rodar o mesmo comando, vemos outra resposta:
origin git@github.com:ehriq/gitsubm_lib.git (fetch)
origin git@github.com:ehriq/gitsubm_lib.git (push)
Isso significa que o diretório analisador é um repositório que funciona de forma autônoma dentro do repositório principal. Portanto, podemos alterar o que quiser. Por exemplo, posso criar um arquivo dentro desse repositório e fazer o commit dele, como podemos ver aqui: https://github.com/ehriq/gitsubm_lib/tree/p3, e o repositório da biblioteca é atualizado de forma independente ao repositório principal.
Mas ao voltar ao diretório da aplicação, temos uma surpresa ao rodar o comando git status:
On branch main
Your branch is up to date with 'origin/main'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
**modified: analisador (new commits)**
no changes added to commit (use "git add" and/or "git commit -a")
O comando mostra que o conteúdo de analisador mudou, e sugere que você faça commit. Mas você pode perguntar “commit? mas eu mudei a lib, não a aplicação” e aí lembramos de algo que eu disse anteriormente: quando eu adiciono um submódulo, eu aponto para um repositório e um commit. Como eu mudei a biblioteca internamente, agora eu preciso atualizar o submódulo para apontar para a versão mais nova. Explicando melhor:
Quando criamos o submódulo, temos um “mapeamento” do repositório principal para uma versão do submódulo.
Quando atualizamos o código dentro do submódulo, estamos trabalhando em um repositório comum do git, portanto é um commit simples.
Mas nesse momento, o repositório aponta para uma versão antiga do submódulo, e por isso temos que atualizar, também, o mapeamento do repositório principal, para que aponte para a versão atual.
Por isso, precisamos fazer o commit do submódulo. Ao fazer o git add e o git commit, e o posterior git push, vemos que a versão do submódulo é alterada, refletindo a versão mais nova.
Aplicando atualizações da biblioteca #
Se outra pessoa fez atualizações na biblioteca, você pode buscar essas alterações entrando no diretório da biblioteca, rodando o comando git pull e, depois, voltando para o diretório do repositório “principal” e rodando o git commit, como indicado na seção anterior, para que o repositório principal saiba qual a versão da biblioteca deve ser usada.
Fazendo clone de um projeto com submódulos #
Ao fazer o git clone de um projeto que usa submódulos, sempre vem a surpresa:
“Eita, não veio o submódulo junto”
Mas isso é esperado. Para resolver, precisamos inicializar os submódulos, e depois fazer o clone de cada um deles, com os comandos:
git submodule init # inicializa os submódulos do repositório
git submodule update # baixa/clona os submódulos
E com esses dois comandos, o repositório estará completo!
Finalizando… #
Isso é apenas uma introdução ao assunto de submódulos, e citei apenas o uso mais básico da funcionalidade, que acredito servir para os casos mais simples.
Para mais informações, pode ver a documentação do comando, a descrição de submódulos no site oficial, além de outros inúmeros materiais disponíveis online.
[1] Nota para os programadores Python: usei o termo biblioteca para identificar as partes de código que podem ser compartilhadas, para evitar confusão com o termo correto, que é módulo.