Como migrar código para um novo repositório e manter o histórico do GIT?

Como migrar código para um novo repositório e manter o histórico do GIT?

O GIT é uma ferramenta de controle de versão bastante difundida no universo dos desenvolvedores de software. De acordo com o portal expandedramblings, o Github – plataforma de hospedagem e gerenciamento de código fonte – atualmente possui cerca de 73 milhões de usuários ativos (números atualizados em 27 de novembro de 2021).

Esta popularidade pode se explicar principalmente por sua intuitividade na utilização e o poder de versionar e gerenciar código-fonte por intermédio de branches para um dado repositório. Gerenciar código-fonte isoladamente, de maneira incremental, amplamente colaborativa – múltiplos usuários enriquecendo a base de código, trabalhando simultaneamente em novas funcionalidades – de maneira simples e objetiva é o que me instiga a, sem pestanejar, iniciar meus projetos de software (de qualquer porte) por um git init.

Os repositórios no GIT podem ser gerenciados de duas maneiras; através de um:

monorepo (mono-repositório): um repositório único que agrega múltiplos projetos, dispostos de maneira modular em uma única estrutura de controle de versão (por exemplo: projeto_a_backend, projeto_a_front_end, projeto_b_backend, projeto_b_frontend); ilustrando:

monorepo
	projeto_a_backend
	    // codigo_fonte_backend
	projeto_a_frontend
	   // codigo_fonte_frontend

OU

multirepo (multi-repositório): como o nome diz, é a representação de múltiplos projetos em múltiplos repositórios (por exemplo: repositório projeto_a_backend, repositório projeto_a_frontend, repositório projeto_b_backend, repositório projeto_b_frontend); ilustrando:

projeto_a_backend
	// codigo_fonte_backend
projeto_a_frontend
	// codigo_fonte_frontend

Inicialmente pode parecer que essas informações não fazem muito sentido, mas acalme-se, pois elas não estão aqui de graça.

Problemática

Em um dos meus desafios no mercado de trabalho, a empresa em que eu atuava costumava gerenciar seus projetos em um monorepo. Em um certo momento dos produtos contidos no monorepo, o gerenciamento dessa maneira parou de fazer sentido – devido ao crescimento do código-fonte, das múltiplas linguagens programação em um mesmo repositório e os desacoplamentos que estavam ocorrendo por desdobramentos em regras de negócio.

Isto posto, a solução mais simples e tentadora seria: criar um novo repositório, copiar os fontes de cada um dos projetos do monorepo para sua correspondência no multirepo e voilá, problema resolvido, não é mesmo? E se eu te disse que a resposta é NÃO?

Ué... mas por que esta não seria a solução? Se o código-fonte está todo lá... quer dizer que não basta só executar o entusiasmante git init e mandar o commit inicial do projeto? O que poderia dar errado? Bom, inicialmente nada... Mas digamos que em um dado ponto de um desses novos repositórios eu precisasse consultar o histórico de modificações de um arquivo específico... E ao consultar o histórico, surpresa: a única modificação do arquivo é o commit inicial do projeto. Nada de histórico, tudo perdido na migração. E agora?

Claramente esse não foi o caminho adotado por mim, pois em hipótese alguma poderia perder a “trajetória do projeto” contida no histórico. A seguir, vou demonstrar como resolvi esse problema!

Solução

Por motivos de confidencialidade não poderei demonstrar o projeto real, migrado naquela situação; para elucidar o estudo de caso, criei dois repositórios hipotéticos: o monorepo, que centraliza os projetos projeto_a_backend e o projeto_a_frontend e os multirepos referente a migração do projeto_a_backend e do projeto_a_frontend. O exercício aqui irá consistir em efetuar a migração do projeto_a_backend e do projeto_a_frontend para fora do monorepo, mantendo exclusivamente os arquivos pertencentes a cada projeto, bem como o histórico de commits deles.

Ao iniciar, a estrutura do repositório monorepo é a seguinte – com o seguinte histórico de commits:

commit f85952b6ee55371b9cee90a8cdd91b969d7cca1b (HEAD -> master)
Author: Kaio Cesar Koerich <kckoerich@MacBook-Pro-de-Kaio-2.local>
Date:   Wed Aug 3 22:43:27 2022 -0300
 
	Edição do codigo_fonte_backend
 
commit 898746ec0d48243e7268a0c9bc10967d26295757
Author: Kaio Cesar Koerich <kckoerich@MacBook-Pro-de-Kaio-2.local>
Date:   Wed Aug 3 22:42:23 2022 -0300
 
	Edição do codigo_fonte_frontend
 
commit 888573ce8db917031e3f7dbe84e2a8f04eb7634c
Author: Kaio Cesar Koerich <kckoerich@MacBook-Pro-de-Kaio-2.local>
Date:   Wed Aug 3 22:40:43 2022 -0300
 
	Criação do projeto_a_frontend
 
commit c802fbf18027432b1c021ab2bf4e69d74b68bf78
Author: Kaio Cesar Koerich <kckoerich@MacBook-Pro-de-Kaio-2.local>
Date:   Wed Aug 3 22:38:52 2022 -0300
 
	Criação do projeto_a_backend

O resultado esperado após a divisão do monorepo deve ser:

Para a migração do projeto_a_backend para o seu próprio repositório, esse novo repositório deverá ter apenas os commits “Criação do projeto_a_backend” e ” Edição do codigo_fonte_backend” e mais um commit adicional referente a divisão do monorepo em multirepos.

Para a migração do projeto_a_frontend para o seu próprio repositório, esse novo repositório deverá ter apenas os commits “Criação do projeto_a_frontend” e “Edição do codigo_fonte_frontend” e mais um commit adicional referente a divisão do monorepo em multirepos.

Para atingir esse resultado, vamos ao passo-a-passo:

Antes de iniciar o processo de migração, crie uma cópia do projeto monorepo e renomeie-a para monorepo_tmp. Isto feito, instale o git-filter-repo (no MacOS, ele pode ser instalado através do brew, executando brew install git-filter-repo).

No diretório projeto monorepo_tmp (cópia do monorepo):

  • execute: git remote rm origin para remover a origem do repositório temporário
  • execute: git filter-repo --path projeto_a_backend --tag-rename '':projeto_a_backend –force para filtrar os commits associados ao projeto_a_backend
  • mova os arquivos e diretórios resultantes do filtro aplicado para a raiz do repositório monorepo_tmp
  • crie um commit para registrar as alterações: git add git commit -am "Divisão do monorepo em multirepos - projeto_a_backend"
  • navegue até o diretório projeto_a_backend
  • execute: git remote add monorepo_tmp ~/Development/revelo/workspace/monorepo_tmp
  • execute: git pull monorepo_tmp master --allow-unrelated-histories
  • execute: git remote rm monorepo_tmp confira o histórico de commits do novo repositório com o git log

O resultado deve ser:

commit acb7b2e59cb53fab2f246e6ef3c581331506ea37 (HEAD -> master, monorepo/master)
Author: Kaio Cesar Koerich <kckoerich@MacBook-Pro-de-Kaio-2.local>
Date:   Wed Aug 3 23:43:55 2022 -0300
 
	Divisão do monorepo em multirepos - projeto_a_backend
 
commit dbd826563f5bed6a219ed2490fb51f019a05cc9c
Author: Kaio Cesar Koerich <kckoerich@MacBook-Pro-de-Kaio-2.local>
Date:   Wed Aug 3 22:43:27 2022 -0300
 
	Edição do codigo_fonte_backend
 
commit c802fbf18027432b1c021ab2bf4e69d74b68bf78
Author: Kaio Cesar Koerich <kckoerich@MacBook-Pro-de-Kaio-2.local>
Date:   Wed Aug 3 22:38:52 2022 -0300
 
	Criação do projeto_a_backend

E a estrutura do projeto_a_backend deve estar da seguinte maneira:

Fazendo o mesmo para o projeto_a_frontend (atentando-se a fazer as devidas substituições nos passos 2 e no segundo bullet do passo 4), o resultado deve ser:

commit ac1340b8143c1433a4e932da9e3ba72c3a6aeaa3 (HEAD -> master)
Author: Kaio Cesar Koerich <kckoerich@MacBook-Pro-de-Kaio-2.local>
Date:   Thu Aug 4 00:11:56 2022 -0300
 
	Divisão do monorepo em multirepos - projeto_a_frontend
 
commit b425a0c990d2711ae40260715916b5e7c48f7e0f
Author: Kaio Cesar Koerich <kckoerich@MacBook-Pro-de-Kaio-2.local>
Date:   Wed Aug 3 22:42:23 2022 -0300
 
	Edição do codigo_fonte_frontend
 
commit 920a9e9f2963396c9ca2529736cd2ef66eb41b40
Author: Kaio Cesar Koerich <kckoerich@MacBook-Pro-de-Kaio-2.local>
Date:   Wed Aug 3 22:40:43 2022 -0300
 
	Criação do projeto_a_frontend

E a estrutura do projeto_a_frontend deve estar da seguinte maneira:

Conclusão

Migrar o projeto copiando e colando o código-fonte de um repositório para o outro é praticamente um crime contra o patrimônio de uma corporação – é sim sobre matar toda a evolução do produto até o momento da migração.

Independente do tamanho de uma companhia, quando se trata de código-fonte é importante que mantenhamos o histórico de commits do GIT em uma eventual migração de repositório, afinal é através deste histórico que podemos acompanhar a evolução do produto, entender algumas das tomadas de decisões efetuadas no projeto, bem como acompanhar a evolução das regras de negócio aplicadas àquele produto.

🛑
As opiniões e comentários expressos neste artigo são de propriedade exclusiva de seu autor e não representam necessariamente o ponto de vista da Revelo. A Revelo Content Network acolhe todas as raças, etnias, nacionalidades, credos, gêneros, orientações, pontos de vista e ideologias, desde que promovam diversidade, equidade, inclusão e crescimento na carreira dos profissionais de tecnologia.