Fernando Nagase

Engenheiro de Software

Git: da pedra aos metais — parte 1

Desenvolvendo meu próprio VCS para desvendar as escolhas por trás do Git

Publicado em 08 jan. de 2024 · Escrito por Fernando

Um dos fatores mais importantes para mim ao aprender sobre um novo assunto é estudar a forma como o entendimento sobre ele evoluiu ao longo da história, pois acredito que, ao fazer isso, muito se revela sobre o contexto em que as pessoas passaram a estudá-lo e a utilidade que ele traz para a sociedade.

Nesse sentido, resolvi experimentar uma abordagem nova para mim, que consiste em criar minhas próprias versões de ferramentas de software comumente usadas. Meu intuito com isso não é apenas replicar cada ferramenta em sua “forma final”, mas compreender as possíveis escolhas que as levaram a ser como são.

É exatamente esse processo que pretendo relatar na série “Git: da pedra aos metais”, que se inicia com esta publicação. Nela, começarei criando a versão mais óbvia que eu conseguir pensar para o versionamento de código e, parte por parte, trarei novas melhorias até atingir um resultado semelhante ao próprio Git.

Ao mesmo tempo em que escrevo cada artigo, planejo realizar a implementação do sistema com a linguagem Rust e disponibilizar o código de cada versão juntamente às publicações. Até o final da série, minha expectativa é que o sistema implemente as funcionalidades a seguir:

  • persistência e restauração de versões (commit);

  • rastreamento de histórico (log);

  • ramificações do histórico (branching);

  • staging area (index).

Eu não recomendaria esta série para pessoas que nunca tiveram contato com o Git, pois pretendo implementar o sistema já levando em consideração alguns dos conceitos existentes em Git. Por outro lado, caso você já o tenha utilizado e queira aprender ainda mais sobre seu funcionamento, essa é a oportunidade!

Primeira abordagem: cada versão é uma cópia completa do repositório

Inicialmente, optei por uma abordagem trivial e que, provavelmente, todos já utilizamos para fazer backup de algum projeto: para cada versão que precisamos guardar, todo o conteúdo presente atualmente no diretório de trabalho é copiado para um novo diretório.

Esse trabalho é automatizado por meio da interação com uma interface de linha de comando (CLI), de forma semelhante ao Git, e essa CLI, nomeada genericamente como vcs, conta com apenas três comandos: init, commit <descricao> e restore <versao>.

Conforme sugerido pelo nome, a responsabilidade do comando init é inicializar um repositório controlado pelo sistema no diretório atual, o que, ao menos por ora, consiste em criar um diretório oculto .vcs para abrigar todos os arquivos relacionados ao controle de versão, de forma análoga ao diretório .git do Git.

O comando commit é responsável por criar o backup do repositório, o que, como discutimos anteriormente, trata-se apenas de copiar todo o conteúdo para um diretório específico dentro de .vcs. Nesse caso, o diretório de cada backup recebe como nome um número sequencial, como 1, 2, 3 etc., indicativo de sua versão.

Para facilitar o trabalho de identificar qual é o número associado à versão atual, utilizei um arquivo version dentro de .vcs, que, a cada execução de commit, tem seu conteúdo substituído pelo número da nova versão.

Finalmente, cada diretório de versão abriga um arquivo extra com o nome README, cujo conteúdo é uma string informada pelo usuário durante o commit (commit <descricao>) para ajudar a identificar as alterações introduzidas por cada um deles.

O último comando, restore <versao>, recebe o encargo de restaurar o repositório para o exato estado em que se encontrava na versão informada, o que, na prática, consiste em apagar todo o conteúdo do diretório raiz do repositório, com exceção de .vcs, e criar uma cópia de todos os arquivos salvos no backup da versão desejada para o diretório atual, com exceção de README.

O conteúdo do diretório .vcs se assemelha ao seguinte:

ls .vcs/
1 2 3 version

O conteúdo do diretório de uma versão específica contém uma cópia de todos os arquivos do próprio repositório juntamente a um arquivo README com a descrição do commit:

ls .vcs/3/
README a.txt b.txt

Tudo o que fiz até aqui é basicamente o que já fazemos ao copiar backups de um projeto para a nuvem ou para um outro dispositivo de armazenamento local, com a única diferença sendo a automatização desse processo por meio da CLI.

Como já sabemos, essa abordagem é prática, mas carrega consigo alguns problemas. Na próxima parte desta série, vou enumerar e resolver alguns desses problemas para melhorar o sistema. Com isso, encerro a primeira parte por aqui! Até a próxima!

Repositório

O código-fonte desta primeira versão pode ser consultado no repositório do GitHub.

Atribuições