2 điểm bởi GN⁺ 2025-07-23 | 1 bình luận | Chia sẻ qua WhatsApp
  • Jujutsu (jj) là một hệ thống quản lý phiên bản cung cấp các khái niệm và câu lệnh đơn giản hơn Git, nhưng vẫn có các tính năng mạnh mẽ
  • Vì dùng Git làm backend nên có ưu điểm là có thể dùng song song hoặc dễ dàng quay lại Git
  • Các tính năng như stacked diff, rebase dễ dàng, revision tạm thời được cung cấp một cách tự nhiên
  • Sử dụng khái niệm bookmark thay cho branch, trực quan hơn với quy trình làm việc thực tế
  • Cách xử lý xung đột linh hoạt, giúp các tác vụ quản lý phiên bản hằng ngày trở nên đơn giản hơn nhiều

Elevator Pitch

Jujutsu (jj) là một hệ thống quản lý phiên bản cung cấp mô hình tư duy và giao diện dòng lệnh đơn giản hơn rất nhiều so với Git.
Không phải đánh đổi tính năng, mà thực ra còn có thể xem là mạnh mẽ hơn
Các tính năng như stacked diff, rebase dễ dàng và revision tạm thời hoạt động một cách tự nhiên
Vì sử dụng Git làm backend, bạn có thể bắt đầu dùng song song với kho Git chỉ bằng một dòng lệnh và có thể quay lại Git bất cứ lúc nào
Điều này cũng không ảnh hưởng đến cách những người dùng khác làm việc với kho lưu trữ

Getting Started

  • Sau khi cài công cụ dòng lệnh jj, nên thiết lập thông tin người dùng và tự động hoàn thành cho shell
  • Khi áp dụng cho kho hiện có, tốt nhất là clone lại mới hoặc bắt đầu sử dụng khi kho đang ở trạng thái không có thay đổi
  • Trong kho hiện có, có thể dùng lệnh jj git init --colocate . để tạo song song kho Git và Jujutsu
  • Dữ liệu kho của jj được lưu trong thư mục .jj/ tách biệt với .git của Git
  • Việc đồng bộ dữ liệu giữa Git và Jujutsu về cơ bản hoạt động ổn định, không có vấn đề gì
  • Tuy nhiên, cần lưu ý lệnh git clean -fdx sẽ xóa thư mục .jj/
  • Việc theo dõi remote branch cũng có thể được thiết lập một lần bằng lệnh đó

How To Use It

Giao diện dòng lệnh của Jujutsu gọn và cô đọng hơn Git
Khái niệm nền tảng thì đơn giản, nhưng quy trình làm việc vẫn cần một chút thời gian để làm quen
Phần dưới đây giải thích các cách dùng chính bằng quy trình ngắn gọn và ví dụ cụ thể

Starting A New Revision

  • Trong Git, công việc mới thường bắt đầu bằng việc tạo branch, nhưng Jujutsu tạo một revision mới để bắt đầu làm việc
  • Cập nhật trạng thái mới nhất từ kho remote: jj git fetch
  • Xem lịch sử kho: jj log
  • Các revision trong lịch sử được phân biệt bằng revision ID riêng của Jujutsuhash commit của Git
  • Bắt đầu công việc mới bằng cách tạo revision mới trên main với jj new main
  • Revision đang được chỉnh sửa hiện tại được ký hiệu bằng @
  • Khi tiền tố trùng nhau biến mất, tiền tố rút gọn của revision ID cũng sẽ thay đổi theo

Making Changes

  • Khi sửa file, thay đổi sẽ được đưa ngay vào revision đó (không có staging area riêng)
  • Khi hoàn tất chỉnh sửa, thêm thông điệp cho revision bằng jj describe -m "메시지"
  • Khi muốn tạo revision mới, dùng jj new (hoặc chỉ định revision trước đó)
  • jj commit là thao tác kết hợp của jj describe + jj new

Navigating

  • Revision rỗng được tạo bằng jj new sẽ tự động bị loại bỏ khi di chuyển sang nơi khác
  • Xóa revision một cách tường minh: jj abandon <rev ID>
  • Hoàn tác việc xóa: jj op undo
  • Quay lại chỉnh sửa revision hiện có bằng jj edit <rev ID>
  • Khi tham chiếu revision, cũng có thể dùng revset expressions:
    • @: revision hiện tại
    • x-: revision cha
    • y+: revision con
    • z::: mọi hậu duệ của z

Branches (Bookmarks)

  • Jujutsu không có trạng thái “đang đứng trên một branch” như Git
  • Thay vào đó, bookmark chỉ là con trỏ trỏ tới một revision cụ thể
  • Tạo bookmark mới: jj bookmark create <tên> -r <revision>
  • Sau khi commit, bookmark không tự động di chuyển, nên khi cần phải di chuyển riêng bằng jj bookmark move <tên> --to <revision>
  • Khi push, dùng cờ --allow-new để cho phép tạo branch mới trên remote
  • Nếu bookmark khác với remote, nó sẽ được đánh dấu bằng * và có thể đồng bộ bằng jj git push

Conflicts

  • Cách xử lý xung đột của Jujutsu linh hoạt hơn Git
  • Khi có xung đột, dấu conflicted sẽ xuất hiện trên revision đó và các revision hậu duệ
  • Trong file xung đột, các conflict marker sẽ được chèn vào, và có thể giải quyết bằng cách xóa các marker đó
  • Có thể sửa trực tiếp hoặc thực hiện thay đổi trong một revision mới rồi gộp lại bằng jj squash

Kết luận

Đến đây là phần hướng dẫn cách xử lý 80% công việc phổ biến nhất trong Git bằng Jujutsu theo cách đơn giản hơn
Trong tương lai, phần sử dụng thông thường/nâng cao sẽ được cung cấp dưới dạng sổ tay tham chiếu nhanh (Quick Reference)
Jujutsu cũng hỗ trợ lệnh jj status giống Git
Mọi câu hỏi hoặc phản hồi có thể gửi qua email được nêu trong bài viết

1 bình luận

 
GN⁺ 2025-07-23
Ý kiến trên Hacker News
  • Với những ai đang phân vân liệu có đáng để học jj hay không, có một điều tôi rất muốn nhấn mạnh.
    Mỗi khi nói về jj trên Hacker News, thường sẽ chia thành hai nhóm: những người chưa thử và những người cực lực khuyên dùng.
    Tôi gần như chưa từng thấy ai dùng jj nghiêm túc trong một tuần rồi lại quay về git.
    Hầu như ai đã thực sự dùng nghiêm túc đều gắn bó với jj.
    Hy vọng hôm nay sẽ là dịp để bạn thử jj.
    Việc chuyển sang dùng nó đơn giản hơn nhiều so với tưởng tượng; ngay trong ngày đầu tôi vẫn giữ nguyên năng suất, và chỉ trong vòng một tuần thì hoàn toàn không cần quay lại các lệnh git nữa.
    Tôi đã làm việc hiệu quả hơn hẳn trong thời gian ngắn, đến mức thấy lạ vì trước đây mình từng dùng git như thế nào.

    • Cá nhân tôi không hề thấy git gây trở ngại gì về mặt năng suất.
      Tôi cũng không dùng lệnh git quá nhiều mỗi ngày, và trong đa số trường hợp thì công việc vẫn diễn ra ổn thỏa.
      Nếu bạn phải thao tác repo rất nhiều bằng git thì jj có thể tốt hơn, nhưng trường hợp của tôi không như vậy.
      Ví dụ này hơi giống như ai đó khăng khăng bảo bạn phải thử bim, một công cụ rất giống vim nhưng có thêm vài tính năng khác.
      Nhưng tôi không tò mò chuyện phải gõ thêm vài lần chữ "i", cũng không cần autocomplete cho Julia.
      Ai thấy jj tốt hơn thì cứ tận hưởng.

    • jj đúng là một bước tiến ở UI của VCS, nhưng với người dùng git nâng cao thì vẫn có vài hạn chế.
      Do chưa hỗ trợ gitattributes nên nếu cần các filter như git-crypt, git-lfs thì sẽ khá bất tiện.
      Khả năng tương thích trên Windows, như xử lý ký tự xuống dòng, cũng có thể kém hơn.
      Ngoài ra, các công cụ ngoài như git-annex hay git-bug không tích hợp với oplog và còn có thể làm lịch sử trở nên lộn xộn.

    • Tôi thực sự đã dùng hơn một tuần rồi quay lại git.
      Cá nhân tôi thích workflow với staging area hơn; đa số mọi người ghét git staging, nhưng với tôi thì lợi ích đó không đủ lớn để đánh đổi trong jj.
      Tôi có thể thay đổi thói quen, nhưng hiện tại vẫn chưa muốn gắn mình với jj.
      Dù vậy, sau này tôi vẫn sẵn sàng thử lại.

    • Lý do tôi dùng jj vài tháng rồi quay lại git là vì vấn đề hiệu năng.
      Các thao tác trong jj mất vài giây, còn git thì phản hồi gần như tức thì bất kể kích thước dự án.
      Ngoài ra khi dùng jj tôi có cảm giác mọi thứ phức tạp hơn một chút về mặt tinh thần; quay lại git thì thấy nhẹ đầu hơn hẳn.
      Có thể điều này chỉ là hệ quả từ việc tôi đã dùng git quá lâu.

    • Ngay cái kiểu nói “dù sao cũng chỉ có hai nhóm người” đã nghe rất giống lời lẽ điển hình của các tín đồ truyền bá jj rồi.

  • Điều khiến tôi phát điên ở jj là mọi thay đổi đều tự động được staging.
    SVN ngày xưa cũng như vậy, còn tôi luôn cảm thấy git cải thiện hẳn nhờ việc staging một cách tường minh.
    Trong repo lúc nào cũng có nhiều thay đổi hơn, và việc chỉ chọn những gì mình muốn đưa vào commit tiếp theo là điều quá tự nhiên trong git.
    Còn với jj (và cả SVN), trước khi commit bạn lại phải làm thủ công những việc rắc rối như tạm chuyển các thay đổi ra ngoài.

    • Vấn đề này với tôi cũng là một thế khó.
      Staging vốn lúc nào cũng phiền, nên ngay cả khi dùng Mercurial tôi cũng thấy mệt.
      Nếu hơn 90% trường hợp đều cần tự động thêm thay đổi thì nó lại khá tiện, nhưng các file không thể đưa vào .gitignore mới là vấn đề.
      Tắt auto add thì bất tiện, mà bật lên cũng lưng chừng.
      Không bên nào thực sự gọn gàng hoàn toàn.

    • Workflow của JJ hơi khác một chút; ví dụ bạn tạo commit nền trước, rồi chồng các commit vô danh lên trên để làm việc.
      Khi sẵn sàng thì dùng jj squash hoặc jj split để sắp xếp lại.

    • Tôi dùng jj commit -i, hoặc tùy chọn -i trong nhiều lệnh khác, cùng với snapshot.auto-track="none()" trong config.
      Hồi dùng Mercurial tôi cũng làm tương tự.
      Trên thực tế, nếu bạn dùng absorb nhiều thì các file hay chunk không cần thiết thường sẽ tự bị bỏ qua.

    • Lệnh jj new chẳng phải rất hợp với workflow mà bạn muốn sao?

    • jj theo dõi đúng cả những branch không nằm ở head trên cây, nên rebase và merge diễn ra mượt mà mà không cần stash.

  • Tôi thấy khó thích nghi với việc tự động thêm thay đổi.
    Có lúc trong quá trình phát triển tôi chỉ tạm sửa một vài file ở local mà không hề định commit chúng.
    Với git thì chỉ cần không staging là có thể yên tâm chúng sẽ không bao giờ bị commit hay push.
    Còn trong jj thì tôi có cảm giác phải unstage gì đó hoặc lúc nào cũng phải cẩn thận.
    Có thể chỉ là vấn đề thói quen, nhưng tôi vẫn thấy thoải mái hơn khi chỉ định rõ những gì mình sẽ commit.
    Tôi cũng tự hỏi không biết có phải mình đang hiểu sai jj hay không.

    • Trong jj bạn có thể dùng jj split để tách các thay đổi.
      Những gì bạn chọn sẽ được đưa vào revision thứ nhất, phần còn lại sẽ vào revision thứ hai.
      Nếu làm nhiều việc cùng lúc rồi chia thành các revision nhỏ gọn thì cách này dễ hơn git rất nhiều.
      Giống như trong git, bạn có thể tạo revision mới bằng jj new, chỉnh sửa xong rồi dùng jj squash -i để chỉ chuyển đúng phần mình muốn.
      Về mặt khái niệm, “@” là revision hiện tại còn “@-” là revision trước đó một bước, nên có thể nghĩ nó như working copy/stage trong git.

    • Việc auto staging trong jj không phải lúc nào cũng là điều tốt.
      Tôi mong tài liệu chính thức của jj và những người hướng dẫn cho người mới sẽ nói rõ hơn rằng có thể tắt mặc định này rất dễ.
      Chỉ cần thêm vào ~/.jjconfig
      [snapshot]
      auto-track = "none()"
      là được.

    • Tôi chủ yếu dùng jj split sau khi làm xong để rà lại và sắp xếp phần sẽ commit.
      Workflow này vận hành khá giống add -p của git.
      Commit trên cùng thường có thể dùng như một working copy không push.
      Ngay cả khi chuyển branch mà không stash, phần đang làm dở vẫn được giữ rất ổn.

    • Đúng vậy, cái thói quen đó với tôi cũng không dễ thay đổi.
      Lập luận hợp lý cho việc phải thay đổi là bạn không nên chạy code ở trạng thái untracked.
      Những thay đổi có ý nghĩa nên được giữ trong commit, còn những gì không cần thì không nên ghi lại, như vậy an toàn hơn.
      Vấn đề nảy sinh khi cấu hình toàn repo và cấu hình local không được tách biệt, mà VSCode là ví dụ điển hình.
      Trong trường hợp đó bạn cần những thay đổi theo từng môi trường mà lại không nên commit, nên mọi thứ càng phức tạp hơn.

    • Nếu bạn muốn workflow kiểu đó, hãy xem “@” của jj như git index, rồi đến lúc commit thì squash vào “@-”; như vậy bạn sẽ có hiệu quả gần giống git add --patch.

  • Tôi đang cố chuyển cả team sang jj nhưng chưa thành công.
    Sẽ rất hay nếu có một trang cho thấy thật trực quan rằng các tác vụ phức tạp trong git có thể làm dễ thế nào bằng jj.
    Kiểu một bài giới thiệu ngắn gọn như elevator pitch.
    Tôi cũng nghĩ có lẽ mình phải tự demo trực tiếp mới được.

    • Có vẻ nhiều tác vụ git mà mọi người hay dùng không hẳn là dễ hơn trong jujutsu.
      Thay vào đó, jujutsu mang đến những workflow mới mà git hoặc không làm được, hoặc làm rất phiền.
      Điều này có thể không dễ chạm tới những lập trình viên đã quá quen với git hiện tại.

    • Tôi cũng không phải người đặc biệt yêu thích git.
      Ngày trước tôi chỉ dùng mercurial, nhưng giờ thì git đã ăn sâu vào đầu mất rồi.
      Dù Jujutsu có điểm mạnh rõ ràng nào đó, tôi vẫn chưa thấy đủ hấp dẫn để bỏ công lo cả phần thiết lập ban đầu như màu sắc phức tạp hay các script quen thuộc.

    • Có một bài so sánh một tác vụ tiêu biểu giữa jj và git đã giúp tôi hiểu ra rất nhiều.
      https://lottia.net/notes/0013-git-jujutsu-miniature.html

    • Tôi cũng gợi ý vài liên kết hữu ích khác.
      https://v5.chriskrycho.com/essays/jj-init/
      https://v5.chriskrycho.com/journal/jujutsu-megamerges-and-jj-absorb/
      https://ofcr.se/jujutsu-merge-workflow

    • Có lẽ bạn sẽ thích phần tiếp theo mà tôi sắp thêm vào bài viết này.
      Tôi định sẽ đăng nó ở cuối trang và cả trong một bài viết riêng trong thời gian tới.

  • Tôi đã dùng jj vài tuần và còn viết cả script tạo commit message tự động.
    Hôm nay tôi định port cùng script đó sang một repo git lâu năm, nhưng nhận ra rằng ở git tôi cần viết code để tự nhận biết khi nào là commit, khi nào là amend.
    Nhờ cấu trúc commit immutable của jj mà script trở nên cực kỳ đơn giản.
    Thế là tôi chỉ việc clone lại repo bằng jj.
    Tôi gần như lập tức thoát khỏi sự lẫn lộn giữa commit và amend.
    https://codeberg.org/jcdickinson/nix/src/branch/main/home/common/scripts/jj-auto.fish

    • Bạn không nhất thiết phải clone lại; hoàn toàn có thể dùng thêm jj trên repo git hiện có.
  • git hoạt động đủ tốt rồi.
    Đúng là có một chút đường cong học tập, nhưng một khi đã quen thì mọi thứ đều hiểu được.
    Thành thật mà nói, trong 95% tình huống chỉ cần biết pull, add, reset, branch và commit là đủ.

    • Trong workflow của tôi, stacked diff là thứ bắt buộc.
      Nó giúp tôi không bị nghẽn vì phải chờ review và vẫn có thể xếp chồng những phần việc tương lai.
      Thiết lập trong git không khó, nhưng thêm thay đổi vào phần dưới của stack rồi restack toàn bộ thì đúng là địa ngục.

    • Khi làm việc nhóm thì việc hiểu rebase/squash là bắt buộc.
      Bạn khó tránh được điều đó trước khi thuyết phục được cả team ngừng dùng chúng.
      Đặc biệt khi nhiều người cùng vội vàng phát triển trên một branch, các thói quen của git bắt đầu trở nên bất tiện.
      Bình thường thì nó ổn, nhưng trong những tình huống phức tạp thì có rất nhiều vấn đề.

    • Chỉ cần từng gặp vài tình huống lạ là bạn sẽ thấy nhược điểm của git.

  • Tôi dùng jj được khoảng 2 tuần và đây là lần đầu tiên tôi thấy thoải mái khi quản lý version chỉ bằng command line.
    Khi dùng git, tôi gần như luôn thao tác bằng GUI (Git Graph trong VSCode), chủ yếu là nhấp chuột phải.
    Còn với jj, chỉ đọc tutorial thôi là tôi đã vào được workflow command line nhất quán ngay.
    Tuy vậy, để khỏi phải liên tục copy-paste id trong log của jj thì có lẽ tôi sẽ phải học ngôn ngữ revset.

    • Tôi cũng ở hoàn cảnh tương tự.
      Dùng git đã lâu nhưng những phần phức tạp tôi luôn xử lý bằng UI.
      Tôi đã dùng riêng CLI của jj gần một tháng và khá hài lòng.
      Dù vậy, tài liệu chính thức của jj có vẻ được viết với giả định rằng người đọc đã có kiến thức nền, nên người mới đôi lúc sẽ thấy rối.
      Tôi mong sẽ có thêm nhiều blog và tutorial hơn.
      Tôi cũng muốn học thêm revset language và rebase.
      Tôi thích CLI gọn gàng, cách giải quyết xung đột và cả oplog.

    • Tôi cũng từng thấy việc copy-paste id rất bất tiện, nhưng từ khi dùng jjui(https://github.com/idursun/jjui) thì mọi thứ mượt hơn hẳn.
      Nó cho cảm giác thao tác rất nhanh, gần như chỉ cần lướt qua log là xong.

  • Theo tôi, tính năng tuyệt nhất của jj là undo.
    Trong git, undo rất khó vì mỗi kiểu sai sót lại cần một lệnh khác nhau.
    Còn với jj thì chỉ cần jj undo là xong.
    Nó cũng không bị ràng buộc vào backend, nên bạn dùng jj một mình ở local cũng không ảnh hưởng gì đến đồng đội.

  • Tôi thấy hơi mơ hồ về việc jj thực chất là gì.
    Không biết nó có phải chỉ là một frontend cho những người thấy git khó hiểu hay không.
    Dù nói là trừu tượng hóa backend, nhưng nó vẫn bị ảnh hưởng quá mạnh bởi git, nên tôi khó hình dung nó hoạt động thế nào với những hệ thống như Perforce hay Piper, nơi commit kiểu số thứ tự là bắt buộc.
    Thiết kế working copy = commit cũng khiến tôi nghĩ rằng không thể kiểm soát chất lượng.
    Ý nghĩa vốn có của commit là chỉ đưa lên những thứ có chủ đích, nhưng với cấu trúc này thì việc phân biệt “commit rác” có vẻ còn khó hơn.
    Cả git lẫn jj dường như đều yếu khi áp dụng cho các dự án lớn, và vẫn không giải quyết được chuyện commit mọc tràn lan.
    Tôi tò mò không biết jj thực sự đang giải quyết vấn đề gì, hay chỉ là một frontend hợp gu của tác giả.

    • Backend Piper thực ra còn vận hành tự nhiên hơn cả backend Git.
      jj commit không tương ứng hoàn toàn 1:1 với git commit, nên không cần hiểu nhầm như vậy.
      Trên thực tế, khi nói “commit” ở đây thì gần hơn với khái niệm “diff có tên”, và trước khi push bạn luôn có thể rất dễ dàng nhào nặn, sắp xếp lại các thay đổi.
      So với rebase hay chỉnh sửa lịch sử trong git thì tiện hơn rất nhiều.
      Tôi thường tạo nhiều commit giữa chừng cho thí nghiệm, tài liệu, v.v., mà nếu là git thì hẳn đã phải nhét tạm vào stash hoặc branch phụ.

    • jj không chỉ là frontend cho git.
      Nó là một hệ thống version control và độc lập với backend.
      Backend tiêu biểu hiện nay là git, nên bạn có thể chuyển sang ngay trên repo hiện có.
      Tôi là người dùng git từ trước cả khi GitHub ra đời, nhưng từ lúc dùng jj thì không thể quay lại git nữa.
      jj đơn giản hơn nhưng vẫn mạnh mẽ.
      working copy = commit thực chất nên được hiểu như “index cũng là một commit”.
      Ví dụ khi bắt đầu làm feature x, bạn có thể tạo commit mới bằng jj new -m "working on feature x" trunk, rồi chồng thêm một commit rỗng lên trên.
      Phần đang làm sẽ nằm trong working copy (@), rồi bạn “chuyển” nó sang commit trước đó (@-) bằng thao tác squash; nhờ vậy thay vì phải dùng những tùy chọn phức tạp như add-p hay reset của git, bạn xử lý mọi thứ bằng diff giữa các commit.
      Chính cấu trúc này giúp việc tách và sắp xếp commit linh hoạt, mạnh hơn git index.
      Vấn đề commit tràn lan thật ra gần hơn với văn hóa pull request, và jj cũng chỉ có thể giải quyết được một phần.

    • working copy không phải cứ thế bị push thẳng lên GitHub; bạn có thể rà soát và sắp xếp lại khi thêm mô tả cho commit.

  • Tôi đã thử jj vài ngày, nhưng hiện vẫn rất hài lòng với lazygit cùng workflow và bộ script hiện tại của mình.
    jj đúng là mới mẻ và khá ổn, nhưng nếu không phải người mới bắt đầu với version control thì tôi chưa thấy đủ động lực để chuyển hẳn.

    • Nếu dùng các công cụ khác như GitButler thì frontend có thể bớt quan trọng hơn, nhưng tôi vừa đưa Jujutsu vào dùng vài ngày trước, hỏi Claude về những thao tác chính như commit, chuyển branch, push/pull GitHub, và chỉ mất 10 phút là nắm được.
      Tôi vẫn dùng diff của Lazygit, nhưng ngay cả khi ở trạng thái detached HEAD cũng chẳng thành vấn đề.
      JJ tương thích rất tốt với git, và giúp xử lý mọi thứ dễ hơn nhiều mà không cần phải nhớ các lệnh phức tạp.
      Việc các file chưa commit tự động đi theo khi chuyển giữa các branch là tính năng tuyệt vời nhất.
      Nó khiến việc qua lại giữa phát triển, sửa lỗi, thay đổi nội dung copy trở nên cực kỳ dễ dàng.
      Nếu AI agent thực hiện thay đổi thì chỉ cần tách worktree ra để dùng.
      Tôi cũng rất thích việc có thể gắn mô tả thay đổi (= commit message) trước khi hoàn tất công việc để nhìn thấy sẵn trên cây.
      Nhìn chung, JJ thật sự rất xuất sắc.