- Để 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-256 và né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 và .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
- Loại đối tượng (“commit”)
- Trạng thái hệ thống tệp tại thời điểm đó (băm cây)
- Commit trước đó (HEAD)
- Tác giả (author)
- 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
Ý 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
Tham khảo: Git Tools - Rerere
Liên kết
git mergekhô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
Ví dụ Pijul làm như vậy
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
cat-fileđể kiểm tra trực tiếp hash ID, khá hayNế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
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
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
Cuối cùng vẫn phải mở bằng chương trình gốc để so sánh
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
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
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
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
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ý