Por que alguém constrói um VCS do zero em 2026? O que você realmente aprende sobre como o Git funciona quando não pode usar ele como muleta? Essa é a história do marsh.

Tem projetos que você faz porque precisa. Tem projetos que você faz porque quer entender de verdade como alguma coisa funciona.
O marsh foi os dois.
O nome vem do meu gato, marshmallow comet. O projeto é um sistema de controle de versão escrito do zero em Rust — formato em disco próprio, SHA-256, compressão zstd, diff de Myers implementado na mão, merge 3-way com LCA no DAG, e uma TUI interativa do grafo de commits chamada comet trail.
Não é um wrapper do Git. O marsh não fala com o Git em nenhum momento. Ele simplesmente existe do zero.
Por que construir quando o Git já existe
Essa é sempre a primeira pergunta.
E a resposta honesta é: porque ler sobre como o Git funciona e implementar como o Git funciona são experiências completamente diferentes.
Eu já tinha lido sobre content-addressable storage, sobre como o Git representa objetos, sobre como o índice funciona por baixo. Entendia o conceito. Mas quando fui implementar — quando precisei tomar decisão sobre cada byte no formato binário do índice, sobre como serializar um objeto Tree, sobre como calcular o LCA de dois commits num DAG — percebi que o que eu achava que entendia era, na melhor das hipóteses, a superfície.
Implementar força você a preencher todas as lacunas que a leitura deixa abertas.
Por que o Git usa SHA-1 (agora migrando para SHA-256)? O que acontece quando dois processos tentam gravar no objeto store ao mesmo tempo? Por que o índice tem um checksum no final? Por que branches são só arquivos de texto com um OID? Essas perguntas só ficam concretas quando você está construindo o sistema e precisa responder cada uma delas.
O marsh foi a minha forma de responder essas perguntas.
O que o marsh consegue fazer
Antes de entrar nos detalhes técnicos, o inventário do que foi construído:
- Object store content-addressable com SHA-256 e compressão zstd em cada objeto
- Formato binário próprio — sem dependência nenhuma do Git
- Staging area binária com checksum SHA-256
- Comandos
add(paralelo via rayon),status,commit,log branch,switch, diff com o algoritmo de Myers implementado do zero- Merge 3-way com cálculo de LCA (Lowest Common Ancestor) no DAG de commits
reset(soft/mixed/hard),tagleve e anotada, reflog- comet trail — TUI interativa do grafo de commits usando ratatui
- CI no GitHub Actions rodando em Linux, Mac e Windows
E tudo isso com um CLI que tem o mesmo feeling ergonômico de usar o Git, porque a interface importa tanto quanto o motor.
A arquitetura em camadas
O projeto é um workspace Rust com dois crates:
marsh-core é a biblioteca — o motor do VCS sem nenhuma I/O de terminal. Ela expõe o modelo de objetos (Blob, Tree, Commit, Tag), o object store, o índice, as refs, o diff, o merge, o grafo. Qualquer coisa que envolve o estado do repositório fica aqui.
marsh-cli é o binário — os subcomandos, a TUI, a apresentação dos resultados. Ele consome a lib, não reinventa nada.
Essa separação não foi pedantismo arquitetural. Foi necessidade prática: quando você está escrevendo testes de integração que inicializam um repositório, fazem commit e verificam o estado, você não quer depender do terminal. E quando você está na TUI lendo o grafo de commits, você quer que a lógica de traversal já esteja testada independentemente da renderização.
A regra é simples: se toca estado de repositório, fica no core. Se toca o terminal, fica no CLI.
O que foi mais difícil de implementar
Surpresa: não foi o merge.
O merge 3-way com LCA foi matematicamente o mais complexo — encontrar o ancestral comum entre dois commits exige um BFS no DAG, e o merge em si precisa lidar com conflitos linha a linha entre três versões do mesmo arquivo. Mas o algoritmo tem uma estrutura clara. Você sabe o que precisa computar.
O que foi mais difícil foi o índice.
O índice do marsh é um arquivo binário que representa o estado do staging area — quais arquivos estão rastreados, qual o OID de cada um, qual o modo (permissão), qual o timestamp do arquivo em disco. Cada entrada precisa ser serializada de forma determinística. O arquivo inteiro tem um checksum SHA-256 no final que cobre todos os bytes anteriores.
O problema não é a serialização em si. O problema é que qualquer bug aqui corrompe silenciosamente o estado de tudo. Se um byte estiver errado no tamanho do nome do arquivo, a próxima leitura do índice vai desserializar lixo — e você só vai descobrir quando o status reportar estado impossível.
Implementar isso me fez entender por que o Git tem tantos comandos de plumbing. Eles existem, em parte, para você conseguir inspecionar o estado interno sem depender de que a camada de porcelana esteja sã.
A TUI do comet trail
A parte mais visualmente satisfatória do projeto foi a TUI — a interface interativa do grafo de commits, que eu chamei de comet trail.
Ela usa ratatui e crossterm, e o que ela faz é renderizar o histórico de commits como um grafo com lanes, exatamente como o git log --graph. Você navega com j/k, pressiona Enter pra ver os detalhes de um commit, e q pra sair.
O desafio técnico aqui foi o lane assignment — o algoritmo que decide em qual coluna do terminal cada commit aparece, de forma que as linhas de parentesco não se cruzem desnecessariamente. Isso é um problema de grafo não trivial quando você tem merges e branches divergentes.
A solução foi um topological sort seguido de um greedy lane allocator: cada commit herda a lane do seu primeiro pai se a lane estiver disponível, e recebe uma nova lane caso contrário. Simples, mas funciona para o caso geral.
E funcionou. O comet trail renderiza históricos reais de repositórios com branches e merges sem cruzamentos desnecessários.
O que esse projeto ensinou que nenhum tutorial ensina
Construir um VCS do zero muda como você usa o Git.
Quando você entende que uma branch é literalmente um arquivo de texto com 64 caracteres hexadecimais dentro, a operação git branch deixa de ser mágica e vira o que é: uma escrita de arquivo. Quando você entende que git add calcula o SHA-256 do conteúdo do arquivo, comprime e grava em .git/objects, e atualiza o índice — você para de pensar em "staging" como um conceito abstrato.
Mas mais do que isso: implementar do zero força você a tomar decisões que o Git tomou por você. Por que SHA-256 e não MD5? Porque colisões de hash em um VCS não são teóricas — são ataques reais. Por que compressão zstd e não sem compressão? Porque um repositório com 10 anos de histórico sem compressão é inviável. Por que índice binário e não texto? Porque o índice é lido e escrito em toda operação de add/status/commit — performance importa.
Cada decisão tem um motivo. E quando você implementa, você precisa entender esses motivos antes de tomar as suas próprias decisões.
O que mudou no meu código após esse projeto: quando trabalho com sistemas que envolvem estado persistente, penso muito mais cuidadosamente sobre o formato de serialização, sobre invariantes de consistência, sobre o que acontece quando um processo é interrompido no meio de uma operação de escrita. Esses são problemas que o marsh me obrigou a resolver na prática.
O próximo passo: packfiles
O objeto store do marsh é loose — cada objeto é um arquivo separado em .marsh/objects/. Isso funciona bem para repositórios pequenos, mas um repositório com histórico longo pode ter dezenas de milhares de objetos loose, cada um com sua própria entrada no sistema de arquivos.
O próximo passo é implementar packfiles — o mecanismo pelo qual o Git empacota múltiplos objetos em um único arquivo binário, com delta compression entre objetos similares. Um blob de 1MB que muda de um commit pro outro não precisa ser armazenado duas vezes inteiro — o segundo pode ser armazenado como um delta do primeiro.
É o problema mais interessante que resta no projeto. Packfiles com delta compression é onde armazenamento endereçável por conteúdo encontra compressão diferencial — e implementar isso do zero vai ser, estou certo, mais um conjunto de decisões que vão mudar como eu penso sobre sistemas de armazenamento.
O marsh está em github.com/Crowlevy/marsh. O README explica como instalar e usar. Feedback é bem-vindo.