4 pontos por GN⁺ 2023-08-02 | 2 comentários | Compartilhar no WhatsApp
  • 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 describe para editar a mensagem de qualquer commit, jj diffedit para editar alterações de um commit sem checkout, jj split para dividir commits e jj squash -i --from X --into Y para 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

 
xguru 2023-08-03

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.

 
GN⁺ 2023-08-02
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 jj oferece
    O 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, no jj log dá para ver o changeset a ser rebaseado e executar algo como jj rebase -s qr -d master, o que é mais rápido do que digitar o nome inteiro do branch

    • Fico curioso sobre o que acontece se eu salvar sem querer um arquivo com um segredo e ele for sugado automaticamente
    • Fico curioso se é usado simplesmente junto com um repositório GitHub
      Dizem 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
    • Fico curioso se há casos de equipes usando isso com sucesso. Quero saber como é quando duas pessoas trabalham no mesmo branch
  • 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...

    • Um projeto pessoal virar um projeto em tempo integral no Google deve dar bastante orgulho
  • 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; com jj split, divide commits; e com jj 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 flags
    O 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 manualmente
    O 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ção

  • Fico 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

    • Vale a pena dar uma olhada no Pijul. Ele basicamente implementa a maior parte do que foi mencionado aqui. O Pijul funciona sobre patches que são CRDTs e trata conflitos como um conceito de primeira classe
    • Fico curioso se você nunca viu valor no git bisect
      Se 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 bisect funciona 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 manual
      Fico curioso sobre como seria possível adaptar o equivalente ao git bisect para 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 sublinhado
    • Fico curioso sobre como isso vai se desenrolar na prática
      Parece 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 automaticamente
    Além disso, é muito mais fácil preparar apenas alguns arquivos no staging e commitá-los do que dividir um commit mais tarde

    • Vejo este projeto como uma contestação da ideia comum de que preparar apenas alguns arquivos no staging e commitá-los é mais fácil do que dividir commits depois
      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
    • Penso o mesmo. Normalmente tenho mais de 10 arquivos extras no diretório do projeto, como arquivos de saída, anotações e scripts descartáveis. Quando coloco arquivos em um commit, sempre escolho pelo nome do arquivo; não incluo tudo que apareceu ou foi alterado
      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.gitignore adicional ao .gitignore. Arquivos ignorados que só eu preciso são muito comuns e não são do tipo que deve entrar no .gitignore do projeto. Existem formas de fazer isso, mas todas são desajeitadas; deveria ser algo simples e oferecido por padrão
    • Quanto ao primeiro problema, acho que bastaria comparar facilmente arquivos já rastreados com arquivos novos
      Ainda 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
    • Pelo que entendi lendo apenas o README, se você adicionar esses arquivos ao .gitignore, eles saem novamente do commit anônimo da cópia de trabalho no próximo commit automático
      O 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
    • Não entendo bem por que essas coisas seriam um problema no design do jj, nem por que preparar apenas alguns arquivos no staging seria mais fácil do que dividir um commit depois
      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ê

    • Sempre preferi lidar com sistemas de controle de versão pela linha de comando, e vou continuar preferindo
      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
    • Pela minha experiência, integrações em IDEs normalmente não implementam todos os recursos do Git CLI. Pode ter melhorado, mas quando se passa do clone/branch/commit/merge básico e há um pouco mais de desenvolvedores e branches, eu acabava voltando ao Git CLI para resolver problemas
    • Não sei se sou um programador de verdade, mas tenho um medo irracional de que uma GUI de Git faça alguma coisa errada. Mesmo no IntelliJ, lido com Git abrindo a CLI embutida. Hábitos antigos custam a morrer
    • Muitas pessoas que estão começando a usar Git não entendem como ele funciona, isto é, o que são commits e branches e o que é possível fazer com eles. Aí culpam a ferramenta de CLI e esperam que, usando o GitHub Desktop, tudo passe a fazer sentido
      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...
    • Na equipe, há apenas dois tipos de pessoas que entendem Git: quem usa a CLI e quem começa a apertar botões sem entender Git
  • 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 jj repositórios Git hospedados no GitHub etc. Por outro lado, fico me perguntando se também há suporte para acessar repositórios Jujutsu nativos via Git
    Como 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?

    • Segundo uma nota de rodapé do README, atualmente há muito poucos motivos práticos para usar o backend nativo
      Esse backend existe principalmente para garantir que, no futuro, seja possível adicionar recursos que não poderiam ser facilmente acrescentados ao backend Git
    • A expectativa parece ser usar sempre o Jujutsu como frontend de um repositório Git, ou então usar um repositório remoto totalmente baseado em Jujutsu
      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”

    • Há uma explicação um pouco mais detalhada aqui: https://github.com/martinvonz/jj/blob/main/docs/technical/co...
      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

    • Seria bom se alguém pudesse explicar melhor essa parte
      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