Alternativas ao submódulo Git: Git Subtree

Nicola Paolucci
Nicola Paolucci
Voltar para a lista

A Internet está repleta de artigos sobre por que você não deve usar os submódulos do Git. Eu concordo com quase tudo, embora eu não seja tão duro na minha avaliação. Os submódulos sejam úteis para alguns casos de uso, mas eles têm várias desvantagens.

Existem alternativas? A resposta é: sim! Existem (pelo menos) duas ferramentas que podem ajudar a rastrear o histórico de dependências de software em seu projeto, permitindo que você continue usando o Git:

Neste post, vou analisar o git subtree e mostrar por que é uma melhoria, embora não perfeita, em relação ao git submodule.

Como exemplo de trabalho, recorro ao meu caso de uso comum. Como faço para armazenar e manter atualizados facilmente os plugins do Vim usados em meus dotfiles?

Por que usar subárvore em vez de submódulo?

Existem várias razões pelas quais você pode achar melhor usar a subárvore:

  • O gerenciamento de um fluxo de trabalho simples é fácil.
  • As versões mais antigas do git são suportadas (ainda mais antigas que a v1.5.2).
  • O código do subprojeto está disponível logo após a conclusão do clone do superprojeto.
  • O git subtree não exige que os usuários do seu repositório aprendam nada de novo. Eles podem ignorar o fato de que você está usando um subtree para gerenciar dependências.
  • O subtree não adiciona novos arquivos de metadados como submódulos doe (ou seja, .gitmodule).
  • O conteúdo do módulo pode ser modificado sem ter uma cópia separada do repositório da dependência em outro lugar.

Na minha opinião, as desvantagens são aceitáveis:

  • Você deve aprender uma nova estratégia de merge ( subtree).
  • Contribuir com o código de volta para os subprojetos é um pouco mais complicado.
  • A responsabilidade de não misturar o código do superprojeto e do subprojeto nos commits é sua.

Como usar o git subtree?

git subtree está disponível na versão stock do git disponível desde maio de 2012 — 1.7.11+. A versão instalada pelo Homebrew no OSX já tem uma subárvore devidamente conectada, mas, em algumas plataformas, você pode precisar seguir as instruções de instalação.

Deixe-me mostrar o exemplo canônico de rastreamento de um plugin do Vim usando o git subtree.

O caminho rápido e sujo sem rastreamento remoto

Se você quiser apenas alguns forros para cortar e colar, basta ler este parágrafo.

Primeiro, adicione a subárvore em uma pasta de prefixo especificada:

 git subtree add --prefix .vim/bundle/tpope-vim-surround https://bitbucket.org/vim-plugins-mirror/vim-surround.git main --squash

(A prática comum é não armazenar todo o histórico do subprojeto em seu repositório principal, mas se você quiser que ele seja preservado, basta omitir a marcação --squash.)

O comando acima produz esta saída:

 git fetch https://bitbucket.org/vim-plugins-mirror/vim-surround.git main warning: no common commits remote: Counting objects: 338, done. remote: Compressing objects: 100% (145/145), done. remote: Total 338 (delta 101), reused 323 (delta 89) Receiving objects: 100% (338/338), 71.46 KiB, done. Resolving deltas: 100% (101/101), done. From https://bitbucket.org/vim-plugins-mirror/vim-surround.git * branch main -} FETCH_HEAD Added dir '.vim/bundle/tpope-vim-surround'

Como você pode ver, essa ação registra um commit de merge ao esmagar todo o histórico do repositório vim-surround em um único:

1bda0bd [3 minutes ago] (HEAD, stree) Merge commit 'ca1f4da9f0b93346bba9a430c889a95f75dc0a83' as '.vim/bundle/tpope-vim-surround' [Nicola Paolucci] ca1f4da [3 minutes ago] Squashed '.vim/bundle/tpope-vim-surround/' content from commit 02199ea [Nicola Paolucci]

Se depois de um tempo você quiser atualizar o código do plugin do repositório upstream, você pode simplesmente fazer um subtree pull:

 git subtree pull --prefix .vim/bundle/tpope-vim-surround https://bitbucket.org/vim-plugins-mirror/vim-surround.git main --squash

É muito rápido e tranquilo, mas os comandos são um pouco longos e difíceis de lembrar. Podemos tornar os comandos mais curtos adicionando o subprojeto como um controle remoto.

Adicionando o subprojeto como um controle remoto

Adicionar a subárvore como um controle remoto nos permite consultá-la de forma mais curta:

git remoto adicionar -f tpope-vim-surround https://bitbucket.org/vim-plugins-mirror/vim-surround.git

Agora podemos adicionar a subárvore (como antes), mas agora podemos nos referir ao controle remoto de forma resumida:

 git subtree add --prefix .vim/bundle/tpope-vim-surround tpope-vim-surround main --squash

O comando para atualizar o subprojeto posteriormente se torna:

 git fetch tpope-vim-surround main git subtree pull --prefix .vim/bundle/tpope-vim-surround tpope-vim-surround main --squash

Contribuindo de volta ao upstream

Agora a gente tem liberdade para fazer o commit das correções para o subprojeto no diretório de trabalho local.

Quando é hora de contribuir de volta para o projeto upstream, precisamos bifurcar o projeto e adicioná-lo como outro controle remoto:

git remote add durdn-vim-surround ssh://git@bitbucket.org/durdn/vim-surround.git

Agora podemos usar o comando subtree push como o seguinte:

 git subtree push --prefix=.vim/bundle/tpope-vim-surround/ durdn-vim-surround main git push using: durdn-vim-surround main Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 308 bytes, done. Total 3 (delta 2), reused 0 (delta 0) To ssh://git@bitbucket.org/durdn/vim-surround.git 02199ea..dcacd4b dcacd4b21fe51c9b5824370b3b224c440b3470cb -} main

Depois disso, estamos prontos e podemos abrir uma pull-request para o mantenedor do pacote.

Sem usar o comando da subárvore

O git subtree é diferente da estratégia de merge de subárvore. Você ainda pode usar a estratégia de merge, mesmo que por algum motivo o git subtree não esteja disponível. Veja como:

Adicione a dependência como um simples git remote:

git remoto adicionar -f tpope-vim-surround https://bitbucket.org/vim-plugins-mirror/vim-surround.git

Antes de ler o conteúdo da dependência no repositório, é importante gravar um merge para que possamos rastrear todo o histórico da árvore do plug-in até este ponto:

 git merge -s ours --no-commit tpope-vim-surround/main

Quais saídas:

O merge automático correu bem; parou antes do commit conforme solicitado

Em seguida, lemos o conteúdo do objeto de árvore mais recente no repositório de plugins em nosso diretório de trabalho pronto para o commit:

 git read-tree --prefix=.vim/bundle/tpope-vim-surround/ -u tpope-vim-surround/main

Agora podemos fazer o commit (e vai ser um commit de merge que vai preservar o histórico da árvore que lemos):

git ci -m"[subtree] adding tpope-vim-surround" [stree 779b094] [subtree] adding tpope-vim-surround

Quando queremos atualizar o projeto, agora podemos extrair usando a estratégia de merge de subtree:

 git pull -s subárvore tpope-vim-surround principal

Conclusão

Depois de ter usado o submódulo por um tempo, eu aprecio muito mais o git subtree, muitos problemas de submódulos são substituídos e resolvidos pela subárvore. Como de costume, com todas as coisas do git, há uma curva de aprendizado para aproveitar ao máximo o recurso.

Siga-me em @durdn e a incrível equipe @Bitbucket para agitar mais o Git.

Pronto(a) para aprender Git?

Tente este tutorial interativo.

Comece agora mesmo