16 điểm bởi GN⁺ 2026-01-29 | 1 bình luận | Chia sẻ qua WhatsApp
  • Để hiểu cấu trúc bên trong của hệ thống quản lý phiên bản, tác giả đã tự triển khai một hệ thống tương tự Git
  • Dùng băm SHA-256nén zstd để thay thế SHA-1 và zlib của Git, đồng thời tổ chức kho lưu trữ bằng cấu trúc thư mục .tvc
  • Được viết bằng Rust, triển khai theo từng bước các tính năng băm tệp · nén · commit · checkout
  • Đối tượng commit bao gồm băm cây, commit cha, tác giả và thông điệp; các tệp giống nhau sẽ không được lưu lại nếu băm trùng lặp
  • Trực tiếp trải nghiệm việc Git là một kho lưu trữ tệp dựa trên địa chỉ nội dung, đồng thời nhấn mạnh tầm quan trọng của định dạng dữ liệu có cấu trúc

Cách băm và nén

  • Git nhận diện mọi đối tượng bằng băm SHA-1, nhưng trong dự án này tác giả dùng SHA-256
    • SHA-1 đã cũ và có điểm yếu về bảo mật, nhưng dự án này chỉ dùng để nhận diện nội dung tệp nên tính bảo mật không quan trọng
  • Thay vì zlib của Git, dự án chọn thư viện nén zstd của Facebook
    • Tác giả đánh giá zstd hiệu quả hơn, và khả năng tương thích với Git không phải mục tiêu
  • Tên dự án là “tvc (Tony’s Version Control)”, sử dụng tệp .tvc.tvcignore tương ứng với cấu trúc của Git

Các bước triển khai

  • Quy trình triển khai theo thứ tự đọc đối số lệnh → đọc quy tắc bỏ qua → in danh sách tệp → băm và nén → tạo cây · commit → quản lý HEAD → checkout commit
  • Được viết bằng Rust; lệnh ls áp dụng quy tắc .tvcignore để đệ quy duyệt các tệp không bị bỏ qua và in ra băm SHA-256 của từng tệp
  • Tính năng nén và giải nén tệp được triển khai đơn giản bằng thư viện zstd

Cấu trúc commit

  • Đối tượng commit bao gồm các thông tin sau
    1. Loại đối tượng (“commit”)
    2. Trạng thái hệ thống tệp tại thời điểm đó (băm cây)
    3. Commit trước đó (HEAD)
    4. Tác giả (author)
    5. Thông điệp commit
  • Khác với Git, dự án bỏ qua việc phân biệt author và committer, đồng thời không triển khai tính năng merge hay rebase
  • Khi tạo commit, đối tượng cây sẽ được tạo, băm, nén và lưu vào .tvc/objects/, sau đó cập nhật tệp HEAD
  • Các tệp giống nhau sẽ không được lưu lại nếu có cùng giá trị băm, nhờ đó tránh lưu trữ trùng lặp

Đối tượng cây và checkout

  • Hàm generate_tree() duyệt thư mục, băm · nén · lưu từng tệp, rồi tạo chuỗi gồm tên tệp và giá trị băm
    • Thư mục con được xử lý đệ quy để hình thành cấu trúc cây
  • Commit và đối tượng cây được phân tích thành các cấu trúc (Commit, Tree) để dễ xử lý trong bộ nhớ
  • Hàm generate_fs() tái tạo lại hệ thống tệp dựa trên cấu trúc cây và thực hiện checkout vào đường dẫn được chỉ định

Bài học từ dự án

  • Tác giả đã trực tiếp trải nghiệm rằng Git là một kho lưu trữ tệp key-value dựa trên địa chỉ nội dung
  • Phần khó nhất là phân tích cú pháp định dạng đối tượng; lần tới tác giả dự định dùng định dạng rõ ràng hơn như YAML hoặc JSON
  • Toàn bộ mã nguồn được công khai trên kho GitHub(tonystr/t-version-control)

1 bình luận

 
GN⁺ 2026-01-29
Ý kiến trên Hacker News
  • Thật thú vị khi Git là SCM duy nhất hỗ trợ recursive merge strategy
    Cách này tự động ghi nhớ lịch sử xử lý xung đột trước đây nên rất hữu ích
    Nhiều người vẫn thích rebase, nhưng khi triển khai merge thì nhất định phải có cơ chế lưu lịch sử xử lý xung đột
    Tham khảo liên quan: Merge made by recursive strategy

    • Ở công ty cũ của tôi, nếu không bật tính năng rerere của git thì nó không nhớ cách giải quyết xung đột trước đó
      Tham khảo: Git Tools - Rerere
    • Có một bài viết của tác giả Mercurial nói về recursive merge
      Liên kết
    • Gần đây tôi mới biết là git merge không có chiến lược “null”
      Ngay cả khi đã xử lý xung đột xong và chỉ muốn lưu lại bản ghi merge, Git vẫn cố giúp đỡ một cách không cần thiết
      Giá mà có một tùy chọn chỉ ghi nhận việc merge mà không đụng vào index hay working tree
    • Cách nguyên tắc hơn để xử lý xung đột là coi chính xung đột như đối tượng hạng nhất của kho lưu trữ
      Ví dụ Pijul làm như vậy
    • Cá nhân tôi ghét git squash
      Không thể xem các lần thử nghiệm qua nhiều commit, khó hoàn tác, và cũng khó tiếp tục làm thêm trên một nhánh đã merge
      Khi nhiều PR là các mảnh ghép của cùng một câu đố, tôi nghĩ merge thông thường tốt hơn nhiều
  • Học về bên trong của công cụ mình dùng mỗi ngày luôn rất thú vị
    Đặc biệt, Git from the Bottom Up là một bài viết tuyệt vời giải thích rõ cấu trúc bên trong của Git
    Chỉ khoảng 20 phút là có thể hiểu được cơ chế hoạt động vốn mơ hồ của các lệnh Git

    • Trước đây tôi đã hiểu Git hoàn toàn nhờ The Git Parable
    • Thật vui khi tìm lại được bài viết từng giúp tôi rất nhiều lúc mới học Git
    • Giờ tôi mới biết có thể dùng lệnh cat-file để kiểm tra trực tiếp hash ID, khá hay
  • Nếu tò mò các coding agent lập kế hoạch thế nào, thì những bài như thế này chính là dữ liệu huấn luyện của chúng
    Nhưng nếu tác giả có dùng LLM hỗ trợ thì lại thành một vòng lặp đệ quy

    • Xem GitHub Insights thì trước khi đăng bài đã có 49 lượt clone và 28 người clone duy nhất
      Có lẽ đang tồn tại các bot cào kho lưu trữ công khai
      Cảm giác mã của mình bị đem đi huấn luyện LLM thật kỳ lạ
      Bản thân bài viết không có đầu ra từ LLM, nhưng tôi có dùng ChatGPT khi cần hỏi về quy ước code Rust hay lời khuyên so sánh thuật toán
    • Ý tưởng làm ô nhiễm LLM bằng một vòng lặp blog tự tham chiếu nghe cũng khá vui
    • Nếu đầu ra của mô hình lại được đưa ngược vào dữ liệu huấn luyện thì sẽ thành vấn đề, nhưng nếu có biên tập bởi con người thì có lẽ vẫn hữu ích đôi chút
    • Nhìn Gemini thỉnh thoảng thể hiện cách nói tiếng Anh mang ngữ điệu Ấn Độ, tôi đoán lượng dữ liệu được tạo ra ở Ấn Độ chắc cực kỳ lớn
    • Viết blog bằng công cụ AI có thể tạo ra vòng lặp kiểu này, nên ngược lại lại có thêm lý do để viết mà không dùng AI
  • Hướng dẫn CodeCrafters “Build your own Git” thực sự rất xuất sắc
    Tôi cũng khuyên xem video live của Jon Gjengset tự triển khai bằng Rust

  • Tôi cũng nghĩ sẽ rất hay nếu quản lý phiên bản được dùng nhiều hơn ngoài lĩnh vực phần mềm
    GotVC là một dự án thú vị với E2E encryption, import song song và cấu trúc hỗ trợ file dung lượng lớn

    • Khi vượt ra ngoài file văn bản, việc xác định khác biệt giữa hai phiên bản trở nên khó hơn nhiều
      Cuối cùng vẫn phải mở bằng chương trình gốc để so sánh
    • Không biết bạn có biết là đã có một dự án tên Game of Trees(Got) rồi không
  • Bài này làm tôi nhớ tới ugit: DIY Git in Python
    Đây là một trong những tài liệu hay nhất, vừa đào sâu vào nội bộ Git vừa giải thích đủ dễ để theo được

    • Thiết kế trang đẹp quá nên tôi đã đánh dấu trang
    • Cùng mạch đó, Write yourself a Git cũng là một tài liệu tôi theo rất vui
    • Tôi từng thử ánh xạ các thao tác Git thành đồ thị Neo4j, và điều đó giúp tôi hiểu cấu trúc tốt hơn rất nhiều
  • Sapling VCS, bản fork Mercurial của Meta, dùng nén Zstd dictionary
    Có thể so sánh với packfile nén delta của Git qua tài liệu giải thích
    Với kho nhỏ, nén delta của Git hiệu quả hơn, nhưng với kho lớn thì nén dictionary dựa trên đường dẫn tốt hơn
    Gần đây Git cũng đã thêm một tính năng tương tự gọi là “path-walk”

  • Tôi cũng từng làm một thứ tương tự, dự án của tôi tên là “shit
    Liên kết GitHub

    • Cái tên “Fast Useful Change Keeper” khá hóm hỉnh
    • Đúng là “THE shit” thật
  • Trước đây tôi từng định làm một SPA framework và đã bị sốc bởi độ phức tạp ẩn bên dưới
    Có lẽ các nhà phát triển React hay Angular cũng từng rơi vào hang thỏ kiểu này
    Git cũng tương tự, nó che giấu sự phức tạp rất khéo

    • Chỉ khi tự tay triển khai Git thì mới thực sự cảm nhận được câu đó có nghĩa gì
  • Tôi thấy một Git client viết bằng PHP, có thể đọc packfile và reftable, đồng thời hỗ trợ diff dựa trên LCS
    gipht-horse

    • Tôi nghĩ kho này là một chiến thắng lớn (W) cho PHP
      Và đây cũng là lần đầu tôi biết có thể dùng @ thay cho HEAD, xét về cú pháp thì khá hợp lý