Jujutsu - sistema de controle de versão distribuído compatível com Git, simples e poderoso
(github.com/martinvonz)- Jujutsu é um sistema de controle de versão usado para copiar código, rastrear mudanças e publicar alterações em projetos de software, projetado para ser fácil de usar tanto para novos usuários quanto para experientes, e tanto para novos projetos pessoais quanto para grandes históricos e equipes
- A interface do usuário e os algoritmos de controle de versão são separados internamente do sistema de armazenamento que fornece o conteúdo; atualmente ele usa repositórios Git como camada de armazenamento, sendo compatível com ferramentas baseadas em Git
- No Git são armazenados apenas commits e arquivos; bookmark (branch) e outros metadados de nível superior são armazenados em um repositório personalizado fora do Git
- O backend Git padrão usa a biblioteca Rust gitoxide, podendo buscar branches de repositórios remotos Git, fazer push para remoto e criar commits que se parecem com commits Git normais
- Usa o modelo working-copy-as-a-commit, no qual a cópia de trabalho é representada como um commit real; alterações de arquivos são registradas automaticamente como um commit normal e depois recebem amend a cada mudança subsequente
- comandos não falham por causa de uma working copy dirty
- não é necessário usar
git stash - é possível definir uma mensagem de commit para o commit da cópia de trabalho mesmo antes de terminar o trabalho
- Todas as operações no repositório são registradas junto com um snapshot do estado do repositório após a operação, permitindo restaurar estados anteriores do repositório, fazer undo por operação e reverter uma operação específica que não seja a mais recente
- Conflitos são registrados como objetos de primeira classe como os commits; operações que geram conflitos também são concluídas com sucesso e depois podem ser tratadas em um fluxo único de resolução de conflitos
- Ao modificar um commit, todos os descendants do commit existente são automaticamente rebased sobre o novo commit; os resultados da resolução de conflitos também são propagados aos descendants, e bookmarks e working copy que apontam para commits rebased também são atualizados
- Não há index nem staging area explícitos; a seleção de commits usa a linguagem revset, e o formato de saída é tratado por uma linguagem de templates configurável pelo usuário
- Os recursos de reescrita de histórico incluem
jj describepara editar a mensagem de qualquer commit,jj diffeditpara editar alterações de um commit sem checkout,jj splitpara dividir commits ejj squash -i --from X --into Ypara mover parte das alterações para outro commit - O recurso de replicação simultânea segura é experimental e foi projetado para evitar que o repositório fique corrompido ao usar sistemas de arquivos distribuídos ou ferramentas de backup como Dropbox e
rsync; no pior caso, o objetivo é expor conflitos entre os estados local e remoto jjé um sistema de controle de versão experimental; a compatibilidade com Git é estável, mas ainda pode haver recursos em desenvolvimento, UX não ideal e lacunas de fluxo de trabalho em certos modos de uso- Os recursos estão bastante completos, mas o suporte a submodules do Git ainda não foi finalizado, há vários bugs de desempenho e não existe suporte nativo para fluxos de trabalho baseados em e-mail
- Antes da versão 1.0.0, estão previstas mudanças de fluxo de trabalho e mudanças incompatíveis com versões anteriores no formato on-disk; para alterações de formato, será tentado oferecer upgrade transparente ou comandos/scripts de upgrade sob demanda
- É um software open source sob licença Apache 2.0, não é um produto com suporte do Google, e o suporte é fornecido pela comunidade
2 comentários
Jujutsu - DVCS compatível com Git, simples e poderoso
Eu postei isso há um ano e meio, e agora voltou a virar assunto no HN.
Opiniões do Hacker News
Fico feliz que tenha aparecido aqui. Migrei para o jj umas 2 ou 3 semanas atrás e não penso em voltar. Tive que mudar bastante o jeito como trabalho mentalmente, mas gosto muito do fluxo que o
jjofereceO tempo de pensar separadamente no que commitar desaparece, e todas as alterações nos arquivos são refletidas automaticamente no commit da cópia de trabalho. Claro que às vezes isso não é desejado, então há recursos para dividir um commit em dois ou mover alterações, e gosto do fato de que todas as operações funcionam sobre commits
O
jj logé ótimo para ver um mapa completo de vários branches, conflitos podem ser resolvidos só parcialmente antes de mudar para outro branch, e os IDs abreviados de changesets também são convenientes. Por exemplo, nojj logdá para ver o changeset a ser rebaseado e executar algo comojj rebase -s qr -d master, o que é mais rápido do que digitar o nome inteiro do branchDizem que pode ser usado com backends como Dropbox, mas seria bom se surgisse um sistema que funcionasse facilmente com IPFS. Para dados grandes, não dá para armazenar 1 TB no GitHub, e estou deixando LFS de fora por ser pago
Quem quer usar um dataset provavelmente já vai tê-lo localmente de qualquer forma, então IPFS, em que a origem do download não é uma só, mas milhares, é uma solução natural. Se usasse IPFS para o armazenamento de dados, eu migraria na hora; caso contrário, acho que vale menos a pena investigar
O Jujutsu começou como um projeto pessoal do autor e agora se tornou o projeto em tempo integral dele no Google. Ele já foi apresentado no Git Merge 2022
Jujutsu: A Git-Compatible VCS - Git Merge 2022:
Vídeo:
https://youtu.be/bx_LGilOuE4
Slides:
https://docs.google.com/presentation/d/1F8j9_UOOSGUN9MvHxPZX...
Venho usando jj diariamente há quase 1 ano, e acho que é definitivamente muito melhor do que a experiência de usuário do Git. Sou exigente em dividir mudanças em commits atômicos, então o rebase automático é um recurso central
Como conflitos podem ser registrados em commits, não é preciso parar no meio de um rebase para corrigir conflitos como no Git ou no Mercurial. Mesmo se você modificar ou excluir um commit, o jj faz automaticamente o rebase de todos os commits descendentes e, diferentemente do Git rebase, também lida com múltiplos filhos, como o Mercurial evolve
Os comandos básicos para manipular o histórico de commits também são bons. Com
jj move|restore, você move ou copia alterações de um commit para outro; comjj split, divide commits; e comjj rebase, move commits. Como tudo fica dentro do histórico de commits, sem working tree nem index, dá para lidar com tudo usando os mesmos comandos e flagsO log de operações também é excelente, então é possível desfazer a maioria das ações sem problemas. Isso contrasta com o Git, em que para consertar um rebase ruim ou um
commit --amendé preciso procurar o commit certo no reflog e ajustar refs manualmenteO ponto mais incômodo é que às vezes o commit automático da cópia de trabalho tenta commitar até arquivos indesejados, e que não há detecção de renomeação, então ele não trata merges com a mesma elegância do Git. Ainda assim, commits quebrados podem ser corrigidos com relativa facilidade usando
jj split|restore, e os desenvolvedores do jj também estão pensando no problema de renomeaçãoFico feliz com o fato de que, mesmo quando o resultado de uma operação gera conflito, essa informação é registrada no commit, a operação é considerada bem-sucedida e o conflito pode ser resolvido depois. Tenho trabalhado com algoritmos de edição colaborativa nos últimos 10 anos, mais ou menos, e idealmente seria bom se ferramentas como Git acabassem sendo substituídas por uma abordagem baseada em CRDT
Com CRDT, seria possível fazer pair programming com a mesma ferramenta, e merges complexos, como autoconflitos que podem ocorrer no Git, também seriam tratados melhor. Também é um modelo mais poderoso
Dito isso, CRDTs modernos de texto resolvem automaticamente edições simultâneas sem conflitos, mas ao colaborar em código offline normalmente queremos que os conflitos apareçam para que uma pessoa os resolva diretamente. Como CRDTs têm mais informações de histórico de edição do que o Git, isso é teoricamente possível, mas, para fazer direito, parece que o próprio conflito teria que entrar no modelo de dados do arquivo de texto, e não sei bem como isso se encaixaria com editores modernos
Parece que o jj encontrou uma técnica parecida, então estou ansioso para ver quão bem isso funciona na prática. Dá a sensação de estar um passo mais perto do sonho de um repositório de código baseado em CRDT
git bisectSe há um bug reproduzível e a causa é complexa, não é útil descobrir qual commit introduziu o bug e ver qual alteração foi a causa? Pelo menos é bom para ter uma primeira intuição do que precisa ser corrigido
Hoje, o
git bisectfunciona melhor quando todos os commits podem ser compilados e executados. Se houver commits com marcadores de conflito que não compilam ou não executam, passa a exigir muito mais intervenção manualFico curioso sobre como seria possível adaptar o equivalente ao
git bisectpara esse cenário. Simplesmente escanear strings de marcadores de conflito pode não ser adequado, porque um documento pode explicar esses marcadores ou, em alguma linguagem de marcação,=======pode ser usado legitimamente como sublinhadoParece entrar em conflito com a ideia de que o histórico de controle de versão é um artefato significativo e de que um conjunto de patches deve ser organizado como uma sequência de mudanças incrementais e atômicas antes de sair da máquina de desenvolvimento
Por outro lado, a maioria dos desenvolvedores ao meu redor já trata o histórico do Git como um histórico infinito de desfazer do editor, então essa abordagem talvez apenas consolide esse fato
Fico curioso sobre como se imagina que o histórico de longo prazo vai funcionar, e se ele será mais ou menos útil do que é hoje
O fato de a cópia de trabalho ser commitada automaticamente parece bom no começo, mas há muitas situações em que isso não é desejável
Se surgir um novo arquivo de artefato e ele ainda não tiver sido colocado no
.gitignore, ele será commitado automaticamente; e se você adicionar um arquivo ignorado em uma branch e depois trocar para outra, ele pode permanecer na cópia de trabalho, mas não estar no.gitignore, e acabar sendo commitado automaticamenteAlém disso, é muito mais fácil preparar apenas alguns arquivos no staging e commitá-los do que dividir um commit mais tarde
Eu também tenho essa ideia gravada na cabeça, mas não sei se ela é realmente correta. Na prática, é bastante comum ajustar o index e fazer amend no commit depois, e isso também poderia ser feito antes
Se, de qualquer forma, não dá para evitar totalmente a edição posterior, talvez seja melhor ter um único fluxo de trabalho para esse tipo de edição. Ainda não usei, mas me parece uma abordagem bastante plausível ao questionar a possibilidade de uma convenção tida como óbvia talvez não ajudar de fato
Não tenho nenhum caso de uso em que todos os arquivos criados ou modificados sempre devam entrar no commit
Quando você troca para outra branch e um arquivo fica na cópia de trabalho, mas não está no
.gitignore, considero isso uma falha do Git. Deveria haver algo como um.local.gitignoreadicional ao.gitignore. Arquivos ignorados que só eu preciso são muito comuns e não são do tipo que deve entrar no.gitignoredo projeto. Existem formas de fazer isso, mas todas são desajeitadas; deveria ser algo simples e oferecido por padrãoAinda assim, é interessante. Nunca gostei do staging do Git, mas não passei pelos casos mencionados aqui, então só agora entendo que é necessária uma etapa intermediária entre o sistema de arquivos e o commit
.gitignore, eles saem novamente do commit anônimo da cópia de trabalho no próximo commit automáticoO fluxo em que o arquivo entra no commit anônimo da cópia de trabalho e depois sai, em vez de simplesmente nunca ser adicionado, não é intuitivo; mas, se esse commit anônimo quase não fica visível para a maioria dos usuários, não deve haver diferença
Pela documentação, vários dos casos que geram preocupação são tratados explicitamente:
https://github.com/martinvonz/jj/blob/main/docs/git-comparis...
Salvo alguns casos especiais, há anos quase não uso Git pela linha de comando. No dia a dia, dependo da integração embutida em IDEs como o IntelliJ, e é difícil entender por que alguém se sujeitaria a fazer isso manualmente
Dentro do editor, posso escolher linhas individualmente para fazer commits parciais, ver todas as branches, fazer merge e cherry-pick, commitar ou fazer amend, dar pull em atualizações e editar tags, tudo de uma vez com atalhos de teclado
Mas parece que aqui sou minoria. Ainda mais considerando o clima de que Git é uma habilidade essencial e que um programador de verdade deveria conseguir digitar os comandos de olhos fechados; fico curioso para saber por quê
Essa habilidade funciona em qualquer ambiente: acessar uma máquina remota e olhar o repositório é igual a fazer isso localmente; não preciso aprender de novo como cada editor integra VCS; e posso continuar usando as ferramentas com as quais já estou acostumado
As tarefas citadas como exemplo são igualmente fáceis pela linha de comando, então não vejo a integração com o editor como algo especial. Se for preciso fazer alguma operação estranha e avançada, como mexer no reflog, é bem provável que você acabe recorrendo à linha de comando de qualquer forma
Claro que não tenho nada contra quem usa GUI. Se a pessoa é produtiva com a ferramenta, não cabe julgar; mas você perguntou o motivo, então respondi. Não estou afirmando que meu jeito é melhor
Esse é o contexto mais comum em que as pessoas dizem que “é preciso aprender o Git CLI”
Alguns dias atrás também houve um exemplo de alguém querendo usar o GitHub Desktop para não aprender comandos Git: https://www.reddit.com/r/learnprogramming/comments/15b7pra/s...
Graças à abordagem que coloca a compatibilidade com Git em primeiro lugar, o Jujutsu parece um candidato a substituto do Git mais forte do que qualquer coisa que vi até agora
Ao ler a explicação sobre compatibilidade com Git, o que me deixou curioso foi que ela parece focar principalmente no caso de uso em que usuários do Jujutsu acessam com
jjrepositórios Git hospedados no GitHub etc. Por outro lado, fico me perguntando se também há suporte para acessar repositórios Jujutsu nativos via GitComo a maioria dos desenvolvedores já está acostumada à CLI do Git, na operação real provavelmente haverá desenvolvedores usando jj e Git juntos na mesma base de código. Ou será que o cenário operacional realista é sempre usar Git como repositório de backend, e não um banco de dados Jujutsu nativo, para permitir acesso pelas duas CLIs?
Esse backend existe principalmente para garantir que, no futuro, seja possível adicionar recursos que não poderiam ser facilmente acrescentados ao backend Git
Para trabalhar e contribuir em um projeto que já usa Jujutsu, faz sentido eu me adaptar ao projeto, e não esperar que o projeto se adapte ao meu fluxo de trabalho
Graças à abordagem de compatibilidade com Git em primeiro lugar, o Jujutsu parece mais forte do que outros candidatos a substituto do Git que vi até agora
Tenho curiosidade sobre o gerenciamento de conflitos. Sei que pmeunier dedicou bastante atenção a formalizar a teoria de patches para o Pijul, e conceitos de patch/conflito pouco sólidos ou problemáticos podem levar a grandes problemas. Acho que foi assim que surgiram os problemas de desempenho do Darcs, não?
Seria bom se a página de comparação na wiki do repositório explicasse o tratamento de conflitos do Pijul com um pouco mais de detalhe do que apenas dizer que é “parecido”
Armazenar conflitos simbolicamente dessa forma permite reproduzi-los posteriormente e até resolver automaticamente certos conflitos, mas não aborda a questão de resolver o conteúdo real do conflito em si
Se alguém implementar isso, talvez seja possível usar o Pijul como backend do jj e obter as vantagens dos dois lados
Replicação segura via rsync, Dropbox e sistemas de arquivos distribuídos — que bom. Hoje em dia estou cada vez mais inclinado a sistemas favoráveis à sincronização simples
Porque você pode simplesmente jogar em qualquer lugar, com qualquer ferramenta, e ele funciona. É sempre bom ver uma ferramenta nova seguir nessa direção
Já ouvi falar que esse tipo de problema existe, mas nunca passei por ele diretamente. Fico curioso sobre quais condições de corrida surgem entre o Git e ferramentas como rsync/Dropbox e acabam causando problemas
Ainda bem que a cópia de trabalho é commitada automaticamente. É um recurso necessário há muito tempo
Meus parabéns ao Martin pelo esforço em criar uma ferramenta elegante