4 điểm bởi GN⁺ 2023-08-02 | 2 bình luận | Chia sẻ qua WhatsApp
  • Jujutsu là một hệ thống quản lý phiên bản dùng để sao chép mã, theo dõi thay đổi và công bố thay đổi trong các dự án phần mềm, được thiết kế để dễ sử dụng cho cả người mới lẫn người có kinh nghiệm, cũng như cho cả dự án cá nhân mới và đội nhóm lớn với lịch sử phát triển đồ sộ
  • Hệ thống này tách biệt nội bộ giữa giao diện người dùng và thuật toán quản lý phiên bản với hệ thống lưu trữ cung cấp nội dung; hiện tại sử dụng kho Git làm tầng lưu trữ nên tương thích với các công cụ dựa trên Git
  • Trong Git chỉ lưu commit và file; bookmark (branch) và các siêu dữ liệu cấp cao khác được lưu trong một kho tùy biến nằm ngoài Git
  • Backend Git mặc định sử dụng thư viện Rust gitoxide; có thể lấy branch từ kho Git từ xa, push lên remote và tạo các commit trông như commit Git thông thường
  • Sử dụng mô hình working-copy-as-a-commit để biểu diễn bản sao làm việc dưới dạng commit thực; thay đổi file được tự động ghi lại thành commit thường và sau đó được amend mỗi khi có thay đổi mới
    • Lệnh không bị thất bại vì working copy đang dirty
    • Không cần git stash
    • Có thể đặt commit message cho commit của working copy ngay cả trước khi công việc hoàn tất
  • Mọi thao tác trong kho đều được ghi lại kèm snapshot trạng thái kho sau thao tác đó, cho phép khôi phục trạng thái kho trước đó, undo theo từng thao tác và revert một thao tác cụ thể không nhất thiết phải là thao tác mới nhất
  • Xung đột được ghi lại như một đối tượng hạng nhất tương tự commit, nên thao tác phát sinh xung đột vẫn thành công và có thể được xử lý sau đó bằng một quy trình giải quyết xung đột thống nhất
  • Khi sửa một commit, toàn bộ descendant của commit đó sẽ tự động được rebase lên trên commit mới; kết quả giải quyết xung đột cũng được lan truyền tới các descendant, đồng thời bookmark và working copy trỏ tới các commit đã rebase cũng được cập nhật
  • Không có index hay staging area tường minh; việc chọn commit dùng ngôn ngữ revset, còn định dạng đầu ra được xử lý bằng ngôn ngữ template do người dùng cấu hình
  • Cung cấp các tính năng viết lại lịch sử như jj describe để sửa message của commit bất kỳ, jj diffedit để chỉnh sửa thay đổi của commit mà không cần checkout, jj split để tách commit, và jj squash -i --from X --into Y để chuyển một phần thay đổi sang commit khác
  • Tính năng sao chép đồng thời an toàn hiện đang ở mức thử nghiệm; được thiết kế để tránh làm kho rơi vào trạng thái hỏng khi dùng hệ thống file phân tán hoặc công cụ sao lưu như Dropbox hay rsync, và trong tình huống xấu nhất hướng tới việc chỉ bộc lộ xung đột giữa trạng thái cục bộ và từ xa
  • jj là một hệ thống quản lý phiên bản thử nghiệm; khả năng tương thích với Git đã ổn định, nhưng vẫn có thể còn các tính năng đang phát triển, UX chưa tối ưu và khoảng trống trong workflow ở một số cách sử dụng nhất định
  • Tính năng nhìn chung đã khá hoàn thiện, nhưng hỗ trợ Git submodule vẫn chưa xong, còn nhiều lỗi hiệu năng và chưa có hỗ trợ native cho workflow dựa trên email
  • Trước phiên bản 1.0.0 sẽ còn có thay đổi workflow và các thay đổi không tương thích ngược đối với định dạng lưu trên đĩa; với các thay đổi định dạng, dự án sẽ cố gắng cung cấp nâng cấp minh bạch hoặc lệnh/script nâng cấp khi được yêu cầu
  • Đây là phần mềm mã nguồn mở theo giấy phép Apache 2.0, không phải sản phẩm được Google hỗ trợ và việc hỗ trợ được cung cấp bởi cộng đồng

2 bình luận

 
xguru 2023-08-03

Jujutsu - hệ thống DVCS tương thích Git đơn giản và mạnh mẽ

Tôi đã đăng bài này từ một năm rưỡi trước, giờ lại thấy nó trở thành chủ đề nổi bật trên HN.

 
GN⁺ 2023-08-02
Ý kiến trên Hacker News
  • Rất vui khi thấy nó được đăng ở đây. Tôi đã chuyển sang jj khoảng 2–3 tuần trước và không có ý định quay lại. Phải thay đổi khá nhiều cách làm việc trong đầu, nhưng tôi khá thích luồng mà jj cung cấp
    Không còn phải dành thời gian nghĩ riêng xem sẽ commit gì; mọi thay đổi trong file đều tự động được phản ánh vào commit bản sao làm việc. Tất nhiên cũng có lúc không muốn như vậy, nên có các chức năng để tách commit thành hai hoặc di chuyển thay đổi, và tôi thích điểm mọi thao tác đều hoạt động trên commit
    jj log rất tuyệt để xem toàn bộ bản đồ của nhiều branch; có thể chỉ giải quyết một phần conflict rồi chuyển sang branch khác; và ID changeset rút gọn cũng tiện. Ví dụ, trong jj log có thể xem changeset cần rebase rồi chạy kiểu jj rebase -s qr -d master, nhanh hơn việc gõ đầy đủ tên branch

    • Tôi tò mò nếu lỡ lưu một file có chứa secret và nó tự động bị hút vào thì sẽ ra sao
    • Tôi tò mò liệu chỉ đơn giản là dùng nó cùng với kho GitHub hay không
      Tài liệu nói có thể dùng với backend như Dropbox, nhưng tôi mong sẽ có một hệ thống dễ dùng với IPFS. Với dữ liệu lớn, không thể lưu 1TB trên GitHub, còn LFS thì tính phí nên tôi loại ra
      Những người muốn dùng dataset dù sao cũng sẽ có nó ở local, nên IPFS, nơi nguồn tải xuống không phải là một mà là hàng nghìn, là lời giải tự nhiên. Nếu dùng IPFS cho kho dữ liệu thì tôi sẽ chuyển ngay, còn nếu không thì tôi nghĩ không đáng để xem xét nhiều
    • Tôi tò mò liệu có trường hợp nào một team đang dùng thành công không. Muốn biết cảm giác ra sao khi hai người cùng làm trên một branch
  • Jujutsu bắt đầu là dự án cá nhân của tác giả, và hiện đã trở thành dự án toàn thời gian của tác giả tại Google. Nó từng được trình bày tại Git Merge 2022
    Jujutsu: A Git-Compatible VCS - Git Merge 2022:
    Video:
    https://youtu.be/bx_LGilOuE4
    Slides:
    https://docs.google.com/presentation/d/1F8j9_UOOSGUN9MvHxPZX...

    • Một dự án cá nhân trở thành dự án toàn thời gian ở Google thì hẳn là khá đáng tự hào
  • Tôi đã dùng jj hằng ngày trong gần 1 năm và thấy chắc chắn tốt hơn nhiều so với trải nghiệm người dùng của Git. Tôi khá nhạy cảm với việc chia thay đổi thành các commit nguyên tử, nên rebase tự động là tính năng cốt lõi
    Vì có thể ghi conflict vào commit, nên không cần dừng giữa chừng khi rebase để sửa conflict như Git hay Mercurial. Dù sửa hoặc xóa commit, jj sẽ tự động rebase tất cả commit hậu duệ, và khác với Git rebase, nó cũng xử lý nhiều commit con giống Mercurial evolve
    Các lệnh cơ bản để thao tác với lịch sử commit cũng tốt. Dùng jj move|restore để chuyển hoặc sao chép thay đổi của một commit sang commit khác, jj split để tách commit, và jj rebase để di chuyển commit. Vì mọi thứ đều nằm trong lịch sử commit, không có working tree hay index, nên có thể xử lý bằng cùng các lệnh và flag
    Nhật ký thao tác cũng rất tuyệt, nên có thể hoàn tác hầu hết công việc mà không gặp vấn đề. Điều này trái ngược với Git, nơi để sửa một rebase tệ hoặc commit --amend, bạn phải lục tìm đúng commit trong reflog rồi sửa ref thủ công
    Điểm bất tiện nhất là commit bản sao làm việc tự động đôi khi cố commit cả những file không mong muốn, và vì không có phát hiện đổi tên, nên xử lý merge không thanh nhã bằng Git. Dù vậy, commit bị hỏng có thể sửa tương đối dễ bằng jj split|restore, và các nhà phát triển jj cũng đang suy nghĩ về vấn đề đổi tên

  • Tôi thích việc ngay cả khi kết quả thao tác sinh ra conflict, nó vẫn ghi thông tin đó vào commit và coi thao tác là thành công, rồi có thể giải quyết conflict sau. Khoảng 10 năm qua tôi đã làm việc với các thuật toán chỉnh sửa cộng tác, và lý tưởng thì các công cụ như Git cuối cùng nên được thay thế bằng cách tiếp cận dựa trên CRDT
    Dùng CRDT thì có thể pair programming bằng cùng một công cụ, và cũng xử lý tốt hơn các kiểu merge phức tạp như self-conflict có thể xảy ra trong Git. Nó cũng là một mô hình mạnh hơn
    Tuy nhiên, CRDT văn bản hiện đại tự động giải quyết chỉnh sửa đồng thời mà không có conflict, trong khi khi cộng tác viết code offline, ta thường muốn conflict lộ ra để con người tự giải quyết. CRDT có nhiều thông tin lịch sử chỉnh sửa hơn Git nên về lý thuyết là có thể, nhưng để làm đúng có lẽ phải đưa chính conflict vào mô hình dữ liệu của file văn bản, và tôi không rõ nó sẽ ăn khớp với các editor hiện đại như thế nào
    Nghe có vẻ jj đã tìm ra một mẹo tương tự, nên tôi mong xem thực tế nó hoạt động tốt đến đâu. Cảm giác như tiến thêm một bước tới giấc mơ về kho mã nguồn dựa trên CRDT

    • Nên xem thử Pijul. Nó trên thực tế đã triển khai hầu hết những gì được nói ở đây. Pijul hoạt động trên các patch vốn là CRDT, và xem conflict là khái niệm hạng nhất
    • Tôi tò mò liệu bạn chưa từng thấy giá trị ở git bisect hay sao
      Nếu có một bug tái hiện được và nguyên nhân phức tạp, tôi nghĩ việc tìm commit nào đã đưa bug vào rồi xem thay đổi nào là nguyên nhân là hữu ích. Ít nhất nó giúp có cảm nhận ban đầu về thứ cần sửa
      Hiện tại git bisect hoạt động tốt nhất khi mọi commit đều có thể build và chạy. Nếu có commit chứa dấu conflict khiến không build hoặc chạy được, sẽ cần can thiệp thủ công nhiều hơn rất nhiều
      Tôi tò mò không biết có thể điều chỉnh chức năng tương đương git bisect trong kịch bản này như thế nào. Chỉ quét chuỗi đánh dấu conflict có thể không phù hợp, vì tài liệu có thể đang mô tả dấu conflict, hoặc trong một số ngôn ngữ markup, ======= có thể được dùng hợp lệ làm gạch chân
    • Tôi tò mò thực tế việc này sẽ diễn ra ra sao
      Nó có vẻ mâu thuẫn với ý tưởng rằng lịch sử quản lý mã nguồn là một sản phẩm đầu ra có ý nghĩa, và các bó patch cần được sắp xếp thành một chuỗi thay đổi tăng dần, nguyên tử trước khi rời máy phát triển
      Tuy nhiên, hầu hết lập trình viên quanh tôi vốn đã xem lịch sử Git như lịch sử undo vô hạn của editor, nên cách tiếp cận này có lẽ chỉ củng cố thực tế đó
      Tôi tò mò mọi người hình dung lịch sử dài hạn sẽ hoạt động thế nào, và liệu nó sẽ hữu ích hơn hay kém hữu ích hơn hiện tại
  • Việc bản sao làm việc được tự động commit thoạt đầu nghe có vẻ hay, nhưng có nhiều tình huống không tốt
    Nếu một tệp đầu ra mới được tạo ra mà bạn chưa kịp đưa vào .gitignore thì nó sẽ bị tự động commit; sau khi thêm tệp bị bỏ qua ở một nhánh rồi chuyển sang nhánh khác, tệp vẫn còn trong bản sao làm việc nhưng không có trong .gitignore, nên có thể bị tự động commit
    Ngoài ra, staging chỉ một số tệp rồi commit dễ hơn nhiều so với việc tách commit về sau

    • Tôi cho rằng dự án này đang thách thức quan niệm phổ biến rằng staging chỉ một số tệp rồi commit thì dễ hơn tách commit về sau
      Bản thân tôi cũng đã ăn sâu suy nghĩ đó trong đầu, nhưng không biết nó có thật sự đúng không. Thực tế là việc sửa index rồi amend commit sau đó diễn ra khá thường xuyên, và cũng có thể làm trước
      Nếu dù sao cũng không thể tránh hoàn toàn việc chỉnh sửa sau, có lẽ một quy trình làm việc duy nhất cho các chỉnh sửa như vậy sẽ tốt hơn. Tôi chưa dùng thử, nhưng đây có vẻ là một cách tiếp cận khá thuyết phục khi đặt câu hỏi rằng những tập quán ta xem là hiển nhiên có thể thực ra không giúp ích gì
    • Tôi cũng nghĩ vậy. Thường trong thư mục dự án có hơn 10 tệp bổ sung như tệp output, ghi chú, script dùng một lần. Khi đưa tệp vào commit, tôi luôn chọn theo tên tệp, chứ không đưa vào mọi thứ mới sinh ra hoặc đã thay đổi
      Tôi không có use case nào mà mọi tệp được tạo/sửa đổi luôn luôn đi vào commit
      Việc khi chuyển sang nhánh khác, tệp vẫn còn trong bản sao làm việc nhưng lại không có trong .gitignore là thất bại của Git theo tôi. Nên có thứ gì đó như .local.gitignore được bổ sung thêm vào .gitignore. Các tệp cần ignore chỉ riêng tôi dùng là chuyện rất phổ biến và không phải loại nên đưa vào .gitignore của dự án. Có cách làm, nhưng tất cả đều gượng gạo, nên cần được cung cấp đơn giản theo mặc định
    • Tôi nghĩ vấn đề đầu tiên có thể chỉ cần so sánh dễ dàng giữa các tệp đã được track và tệp mới là được
      Dù vậy vẫn thú vị. Tôi chưa từng thích staging của Git, nhưng cũng chưa gặp trường hợp được nói ở đây, nên giờ mới hiểu rằng cần có một bước trung gian giữa hệ thống tệp và commit
    • Chỉ đọc README thì tôi hiểu cấu trúc là: nếu thêm các tệp đó vào .gitignore, chúng sẽ lại bị loại khỏi commit bản sao làm việc ẩn danh ở lần auto-commit tiếp theo
      Luồng “được đưa vào commit bản sao làm việc ẩn danh rồi bị loại ra” thay vì ngay từ đầu không được thêm vào không trực quan lắm, nhưng nếu commit ẩn danh đó hầu như không hiện ra với người dùng thì có lẽ cũng không khác biệt
    • Tôi không rõ vì sao những thứ này lại là vấn đề trong thiết kế của jj, và cũng không rõ vì sao staging một số tệp lại dễ hơn tách commit về sau
      Tài liệu có đề cập rõ ràng đến nhiều trường hợp đáng lo ngại:
      https://github.com/martinvonz/jj/blob/main/docs/git-comparis...
  • Ngoại trừ vài trường hợp đặc biệt, nhiều năm nay tôi hầu như không dùng Git trên dòng lệnh. Hằng ngày tôi dựa vào tích hợp sẵn trong IDE như IntelliJ, và khó hiểu vì sao phải tự chịu cảnh làm thủ công
    Ngay trong editor có thể chọn theo từng dòng để commit một phần, xem tất cả nhánh, merge và cherry-pick, commit hoặc amend, pull cập nhật, chỉnh sửa tag, tất cả chỉ bằng một phím tắt
    Tuy nhiên ở đây có vẻ tôi thuộc thiểu số. Nhất là khi tính đến bầu không khí kiểu Git là kỹ năng bắt buộc nên lập trình viên thật sự phải nhắm mắt cũng gõ được lệnh, tôi càng tò mò vì sao lại như vậy

    • Tôi luôn thích xử lý hệ thống quản lý phiên bản bằng dòng lệnh và về sau cũng sẽ vẫn vậy
      Kỹ năng này không phụ thuộc môi trường: kết nối vào máy từ xa để xem repository cũng giống như local, không cần học lại cách tích hợp VCS của từng editor, và có thể dùng nguyên các công cụ quen thuộc
      Những tác vụ được nêu làm ví dụ cũng có thể làm dễ dàng y như vậy trên dòng lệnh, nên tôi không thấy tích hợp editor có gì đặc biệt. Nếu cần các thao tác lạ và nâng cao kiểu đụng đến reflog, cuối cùng khả năng cao vẫn phải mở dòng lệnh
      Tất nhiên tôi không có phàn nàn gì với người dùng GUI. Nếu họ năng suất với công cụ đó thì không có gì để phán xét; chỉ là bạn hỏi vì sao nên tôi trả lời, chứ không khẳng định cách của tôi tốt hơn
    • Theo kinh nghiệm của tôi, tích hợp IDE thường không triển khai toàn bộ chức năng của Git CLI. Có thể giờ đã khá hơn, nhưng một khi vượt qua clone/branch/commit/merge cơ bản và số lập trình viên cùng nhánh tăng lên một chút, cuối cùng khi xử lý vấn đề vẫn phải quay lại Git CLI
    • Không biết có phải lập trình viên thật sự không, nhưng tôi có nỗi sợ phi lý rằng Git GUI có thể làm sai việc gì đó. Ngay cả trong IntelliJ tôi cũng mở CLI tích hợp để dùng Git. Thói quen cũ khó bỏ
    • Rất nhiều người mới bắt đầu dùng Git không hiểu Git hoạt động thế nào, tức commit và branch là gì và có thể làm gì. Rồi họ đổ lỗi cho công cụ CLI và kỳ vọng rằng dùng GitHub Desktop thì mọi thứ sẽ trở nên dễ hiểu
      Đây là bối cảnh phổ biến nhất khi người ta nói “phải học Git CLI”
      Vài ngày trước cũng có ví dụ dùng GitHub Desktop để khỏi phải học lệnh Git: https://www.reddit.com/r/learnprogramming/comments/15b7pra/s...
    • Trong nhóm, chỉ có hai kiểu người hiểu Git. Người dùng CLI, và người bắt đầu bấm nút mà không hiểu Git
  • Nhờ cách tiếp cận đặt khả năng tương thích với Git lên trước, Jujutsu trông có vẻ là ứng viên thay thế Git mạnh hơn trong số những gì tôi đã thấy đến nay
    Khi đọc phần giải thích về khả năng tương thích với Git, điều tôi thắc mắc là có vẻ nó chủ yếu tập trung vào trường hợp người dùng Jujutsu truy cập các kho Git được host trên GitHub, v.v. bằng jj. Ngược lại, tôi cũng tò mò liệu có hỗ trợ cách truy cập kho Jujutsu native bằng Git hay không
    Vì hầu hết lập trình viên đã quen với Git CLI, trong vận hành thực tế có lẽ sẽ có các lập trình viên dùng cả jj lẫn Git trên cùng một codebase. Hoặc tôi tự hỏi liệu kịch bản vận hành thực tế có phải là luôn dùng Git làm kho lưu trữ backend thay vì cơ sở dữ liệu Jujutsu native, để cả hai CLI đều truy cập được hay không

    • Theo chú thích trong README, hiện tại gần như không có lý do thực tế nào để dùng backend native
      Backend này chủ yếu tồn tại để bảo đảm rằng sau này có thể thêm các tính năng vốn không dễ thêm vào backend Git
    • Có lẽ kỳ vọng sẽ là luôn dùng Jujutsu như một frontend cho kho Git, hoặc dùng một kho từ xa hoàn toàn dựa trên Jujutsu
      Nếu làm việc và đóng góp cho một dự án đã dùng Jujutsu, thì việc tôi thích nghi với dự án sẽ hợp lý hơn là dự án phải thích nghi với workflow của tôi
  • Nhờ cách tiếp cận ưu tiên tương thích với Git, Jujutsu trông mạnh hơn trong số các ứng viên thay thế Git mà tôi đã thấy đến nay
    Tôi tò mò về quản lý xung đột. Tôi biết pmeunier đã bỏ nhiều công sức để hình thức hóa lý thuyết patch cho Pijul, và các khái niệm patch/xung đột không lành mạnh hoặc có vấn đề có thể dẫn đến rắc rối lớn. Hình như vấn đề hiệu năng của Darcs cũng phát sinh như vậy
    Sẽ tốt hơn nếu trang so sánh trong wiki của kho giải thích cách xử lý xung đột của Pijul chi tiết hơn một chút, thay vì chỉ nói là “tương tự”

    • Ở đây có phần giải thích chi tiết hơn một chút: https://github.com/martinvonz/jj/blob/main/docs/technical/co...
      Việc lưu trữ xung đột một cách ký hiệu như thế này cho phép tái hiện xung đột về sau và thậm chí có thể tự động giải quyết một số xung đột nhất định, nhưng không xử lý đến vấn đề giải quyết chính nội dung xung đột thực tế
      Nếu có ai triển khai, có lẽ cũng có thể dùng Pijul làm backend cho jj để có được ưu điểm của cả hai bên
  • Sao chép an toàn qua rsync, Dropbox, hệ thống tệp phân tán nghe hay đấy. Dạo này tôi ngày càng nghiêng về các hệ thống thân thiện với đồng bộ hóa đơn giản
    Vì chỉ cần ném nó vào đâu đó bằng bất kỳ công cụ nào là nó cứ hoạt động. Một công cụ mới đi theo hướng này luôn là điều đáng hoan nghênh

    • Sẽ rất tốt nếu có thể giải thích thêm phần này
      Tôi từng nghe nói có vấn đề như vậy nhưng chưa trực tiếp gặp phải. Tôi tò mò giữa Git và các công cụ như rsync/Dropbox sẽ phát sinh điều kiện tranh chấp nào gây ra vấn đề
  • Bản sao làm việc được commit tự động thì thật tốt. Đây là tính năng đã cần từ lâu
    Xin vỗ tay cho Martin vì đã bỏ công sức tạo ra một công cụ thanh lịch