16 pontos por GN⁺ 2026-01-29 | 1 comentários | Compartilhar no WhatsApp
  • Para entender a estrutura interna de um sistema de controle de versão, implementei diretamente um sistema semelhante ao Git
  • Usei hash SHA-256 e compressão zstd para substituir o SHA-1 e o zlib do Git, organizando o repositório com a estrutura de diretórios .tvc
  • Escrito em Rust, implementa em etapas as funções de hash de arquivos, compressão, commit e checkout
  • O objeto de commit inclui hash da árvore, commit pai, autor e mensagem, e arquivos idênticos não são salvos novamente graças à deduplicação por hash
  • Ao vivenciar na prática que o Git é um armazenamento de arquivos baseado em endereçamento por conteúdo, o projeto destaca a importância de formatos de dados estruturados

Método de hash e compressão

  • O Git identifica todos os objetos com hash SHA-1, mas neste projeto foi usado SHA-256
    • O SHA-1 é antigo e tem vulnerabilidades de segurança, mas neste projeto ele seria usado apenas para identificar o conteúdo dos arquivos, então a segurança não era importante
  • Em vez do zlib do Git, foi adotada a biblioteca de compressão zstd da Facebook
    • Considerei o zstd mais eficiente, e compatibilidade com o Git não era o objetivo
  • O nome do projeto é “tvc (Tony’s Version Control)”, usando os arquivos .tvc e .tvcignore como equivalentes às estruturas correspondentes do Git

Etapas da implementação

  • O processo de implementação seguiu a ordem leitura dos argumentos de comando → leitura das regras de ignorar → exibição da lista de arquivos → hash e compressão → criação de árvore e commit → gerenciamento do HEAD → checkout do commit
  • Escrito em Rust, o comando ls aplica as regras de .tvcignore, percorre recursivamente os arquivos não ignorados e imprime o hash SHA-256 de cada arquivo
  • Com a biblioteca zstd, a funcionalidade de compressão e descompressão de arquivos foi implementada de forma simples

Estrutura do commit

  • O objeto de commit inclui as seguintes informações
    1. tipo do objeto (“commit”)
    2. estado do sistema de arquivos naquele momento (hash da árvore)
    3. commit anterior (HEAD)
    4. autor (author)
    5. mensagem de commit
  • Diferentemente do Git, a distinção entre autor e committer foi omitida, e não foram implementadas funções de merge ou rebase
  • Ao criar um commit, o objeto de árvore é gerado, hasheado, comprimido e armazenado em .tvc/objects/, e o arquivo HEAD é atualizado
  • Arquivos idênticos não são salvos novamente quando o hash é o mesmo, permitindo evitar armazenamento duplicado

Objeto de árvore e checkout

  • A função generate_tree() percorre diretórios, faz hash, comprime e armazena cada arquivo, e monta uma string com nome do arquivo e hash
    • Subdiretórios são processados recursivamente para formar a estrutura de árvore
  • Os objetos de commit e árvore são analisados em structs (Commit, Tree) para facilitar seu tratamento em memória
  • A função generate_fs() recria o sistema de arquivos com base na estrutura de árvore e executa o checkout no caminho especificado

Lições do projeto

  • Foi possível vivenciar na prática que o Git é um armazenamento de arquivos baseado em endereçamento por conteúdo (key-value)
  • A parte mais difícil foi o parsing do formato dos objetos, e no futuro a ideia é usar um formato mais claro, como YAML ou JSON
  • Todo o código está disponível no repositório GitHub (tonystr/t-version-control)

1 comentários

 
GN⁺ 2026-01-29
Comentários no Hacker News
  • É interessante que o Git seja o único SCM que oferece suporte à recursive merge strategy
    Esse método é muito útil porque lembra automaticamente resoluções de conflito do passado
    Muita gente ainda prefere rebase, mas ao implementar merge é indispensável incluir um mecanismo de armazenamento do histórico de resolução de conflitos
    Referência relacionada: Merge made by recursive strategy

    • No meu trabalho anterior, se a função rerere do git não estivesse ativada, ele não lembrava resoluções de conflitos anteriores
      Referência: Git Tools - Rerere
    • Há um texto escrito pelo autor do Mercurial sobre recursive merge
      Link
    • Descobri recentemente que o git merge não tem uma estratégia “null”
      Mesmo quando você já resolveu os conflitos e só quer deixar registrado que houve um merge, o Git ainda tenta ajudar desnecessariamente
      Seria bom ter uma opção que apenas registrasse o merge sem mexer no índice nem na working tree
    • Uma forma mais principiológica de lidar com conflitos é tratar o próprio conflito como um objeto de primeira classe no repositório
      Por exemplo, o Pijul faz isso
    • Pessoalmente, eu odeio git squash
      Você não consegue ver as tentativas feitas em vários commits, é mais difícil reverter, e também complica continuar trabalhando em uma branch já mergeada
      Quando vários PRs são peças de um mesmo quebra-cabeça, acho que um merge simples é muito melhor
  • Aprender o funcionamento interno de uma ferramenta que usamos todos os dias é sempre divertido
    Em especial, Git from the Bottom Up é um excelente texto que explica com clareza a estrutura interna do Git
    Em uns 20 minutos, dá para entender os mecanismos opacos por trás dos comandos do Git

    • Antigamente, foi com The Git Parable que eu passei a entender Git de verdade
    • Fiquei feliz de reencontrar um texto que tinha me ajudado muito quando estava aprendendo Git pela primeira vez
    • Só agora descobri que dá para inspecionar diretamente os IDs de hash com o comando cat-file, e isso é bem legal
  • Se você tem curiosidade sobre como agentes de programação fazem planejamento, textos como este são parte dos dados de treinamento deles
    Mas, se o autor teve ajuda de um LLM, isso também pode virar uma situação circular

    • Olhando o GitHub Insights, já havia 49 clones e 28 clonadores únicos antes mesmo de o texto ser publicado
      Parece que realmente existem bots raspando repositórios públicos
      É uma sensação estranha pensar que meu código pode estar sendo usado no treinamento de LLMs
      O texto em si não tem saída de LLM, mas usei ChatGPT para pedir conselhos sobre convenções de código em Rust e comparação de algoritmos
    • Também parece uma ideia divertida poluir LLMs com um loop autorreferencial de blogs
    • Se a saída do modelo voltar a entrar como dado de treinamento, isso pode ser problemático, mas com edição humana talvez fique um pouco útil
    • Quando o Gemini às vezes soa como inglês falado com sotaque indiano, dá a impressão de que deve haver uma quantidade enorme de datasets gerados na Índia
    • Como esse tipo de ciclo pode surgir ao escrever blogs com ferramentas de IA, isso acaba virando até um motivo para escrever sem IA
  • O tutorial CodeCrafters “Build your own Git” é realmente excelente
    Também recomendo o vídeo ao vivo do Jon Gjengset, em que ele implementa em Rust

  • Eu também gostaria que controle de versão fosse mais usado fora do desenvolvimento de software
    O GotVC é um projeto interessante, com criptografia E2E, importação paralela e estrutura para suportar arquivos grandes

    • Passando de arquivos de texto, fica difícil descobrir a diferença entre duas versões
      No fim, você acaba tendo que abrir no programa original para comparar
    • Fiquei curioso se você conhece um projeto que já existe chamado Game of Trees(Got)
  • Este texto me lembrou ugit: DIY Git in Python
    É um dos melhores materiais para mergulhar fundo no funcionamento interno do Git sem deixar de ser fácil de acompanhar

    • O design da página é tão bonito que eu favoritei
    • Na mesma linha, também foi divertido acompanhar Write yourself a Git
    • Eu já tentei mapear operações do Git em um grafo Neo4j, e isso ajudou bastante a entender a estrutura
  • O Sapling VCS, fork do Mercurial feito pela Meta, usa compressão com dicionário Zstd
    Dá para comparar com o packfile comprimido por delta do Git na documentação explicativa
    Em repositórios pequenos, a compressão delta do Git é mais eficiente, mas em repositórios grandes a compressão por dicionário baseada em caminho é melhor
    Recentemente, uma função semelhante de “path-walk” também foi adicionada ao Git

  • Eu também fiz uma tentativa parecida, e o nome do meu projeto é “shit
    Link do GitHub

    • O nome “Fast Useful Change Keeper” é bem espirituoso
    • É realmente “THE shit”
  • Lembrei de quando tentei criar um framework SPA e me surpreendi com a complexidade escondida
    Imagino que desenvolvedores de React ou Angular também devam passar por esse tipo de toca do coelho
    O Git também esconde muito bem sua complexidade

    • Só quando você tenta implementar Git por conta própria é que entende de verdade o que isso quer dizer
  • Vi um cliente Git escrito em PHP que consegue ler packfile e reftable, além de oferecer diff baseado em LCS
    gipht-horse

    • Acho que esse repositório é uma grande vitória (W) para o PHP
      E também foi a primeira vez que descobri que dá para usar @ no lugar de HEAD, o que sintaticamente parece bem razoável