Git Worktree vào hệ sinh thái Microservices Bài 2: Giải phẫu Git Worktree: Một thư mục .git, đa không gian làm việc
Để sử dụng một công cụ một cách làm chủ hoàn toàn trong hệ thống lớn, chúng ta không chỉ học thuộc câu lệnh mà cần hiểu rõ cơ chế hoạt động bên dưới (Under the hood). Tại sao Git Worktree có thể tạo ra một thư mục code mới chỉ trong tích tắc mà không tốn thêm dung lượng lưu trữ lịch sử commit? Câu trả lời nằm ở kiến trúc của thư mục .git.
1. Ôn lại cấu trúc của một Git Repository truyền thống
Khi bạn chạy lệnh git clone hoặc git init cho một Microservice (ví dụ payment-service), Git sẽ tạo ra một thư mục ẩn .git nằm ngay bên trong thư mục dự án. Lúc này, dự án của bạn gồm 2 thành phần gắn chặt với nhau:
-
Working Tree (Không gian làm việc): Chính là các thư mục
app/,src/,config/... nơi bạn trực tiếp mở IDE lên để đọc và sửa code. -
Git Directory (Thư mục
.git): Là "bộ não" và "nhà kho" lưu trữ toàn bộ lịch sử dự án.
Bên trong thư mục .git truyền thống có hai nhóm dữ liệu cực kỳ quan trọng mà bạn cần phân biệt:
Plaintext
payment-service/.git/
├── objects/ <-- [NẶNG] Kho chứa toàn bộ lịch sử code, các bản ghi commit (nặng hàng trăm MB - vài GB)
├── refs/ <-- [NHẸ] Danh sách các con trỏ nhánh (branches) và tags
├── config <-- [NHẸ] Cấu hình remote (origin), user...
│
├── HEAD <-- [TRẠNG THÁI RIÊNG] Đang trỏ tới nhánh nào? (VD: ref: refs/heads/feature/loyalty-v2)
└── index <-- [TRẠNG THÁI RIÊNG] Staging Area - Những file nào đang được `git add`?
Điểm mấu chốt nằm ở đây: Trong mô hình truyền thống, mỗi thư mục .git chỉ có duy nhất 1 file HEAD và 1 file index. Đó là lý do tại một thời điểm, thư mục payment-service chỉ có thể đứng ở một nhánh duy nhất. Muốn sang nhánh khác, bạn buộc phải ghi đè lên HEAD và index hiện tại (thông qua git checkout).
2. Cơ chế của Git Worktree: Tách "Trạng thái riêng" khỏi "Nhà kho chung"
Các kỹ sư thiết kế Git đã đặt ra câu hỏi: "Tại sao phải nhân bản cả kho objects/ nặng hàng Gigabyte chỉ để có thêm một cặp file HEAD và index mới?"
Từ phiên bản Git 2.5, tính năng Multiple Worktrees ra đời với cơ chế cực kỳ thông minh: Giữ nguyên "nhà kho" (objects, refs, config) ở thư mục gốc, và chỉ tạo thêm các bộ (HEAD, index) mới cho từng không gian làm việc phụ.
Khi bạn đứng ở thư mục gốc payment-service và tạo thêm một worktree mới tên là payment-hotfix, cấu trúc trên ổ cứng sẽ biến đổi như sau:
Plaintext
workspace/
│
├── payment-service/ <-- [MAIN WORKTREE] (Đang ở nhánh feature/loyalty-v2)
│ ├── src/
│ └── .git/ <-- Thư mục .git thật sự (Chứa toàn bộ dữ liệu)
│ ├── objects/ (Dùng chung cho mọi worktree)
│ ├── refs/ (Dùng chung cho mọi worktree)
│ ├── HEAD (Trỏ tới: feature/loyalty-v2)
│ ├── index (Staging của feature/loyalty-v2)
│ └── worktrees/ <-- [MỚI] Nơi quản lý các worktree phụ!
│ └── payment-hotfix/
│ ├── HEAD (Trỏ tới: hotfix/payment-stuck)
│ ├── index (Staging riêng của hotfix/payment-stuck)
│ └── gitdir (Lưu đường dẫn ngược về thư mục payment-hotfix)
│
└── payment-hotfix/ <-- [LINKED WORKTREE] (Đang ở nhánh hotfix/payment-stuck)
├── src/
└── .git <-- CHÚ Ý: Đây là 1 FILE TEXT (không phải thư mục!)
3. Bất ngờ lớn nhất: .git trong Worktree phụ chỉ là một File Text!
Nếu bạn đi vào thư mục payment-hotfix vừa tạo bằng Worktree và kiểm tra dung lượng của .git, bạn sẽ thấy nó chỉ nặng đúng... vài chục Bytes!
Bởi vì trong các Worktree phụ (Linked Worktrees), .git không phải là một thư mục, mà chỉ là một file văn bản thuần túy đóng vai trò như một đường dẫn chỉ đường (pointer). Nếu bạn mở file .git này ra xem bằng lệnh cat .git, bạn sẽ thấy nội dung duy nhất bên trong là:
Plaintext
gitdir: /Users/username/workspace/payment-service/.git/worktrees/payment-hotfix
Luồng hoạt động khi bạn gõ lệnh Git ở thư mục payment-hotfix:
-
Bạn gõ
git statushoặcgit commitbên trongpayment-hotfix. -
Git đọc file
.git(nặng vài chục bytes) để biết trạng tháiHEADvàindexcủa nhánh hotfix này đang nằm ở.../payment-service/.git/worktrees/payment-hotfix. -
Khi cần lưu commit mới hoặc tra cứu lịch sử code, Git tự động đi ngược lên một cấp để truy cập vào kho
objects/vàrefs/chung củapayment-service/.git.
4. Ba lợi ích "vàng" từ kiến trúc này đối với hệ thống Microservices
Nhờ cơ chế "Một nhà kho, nhiều con trỏ trạng thái", việc vận hành hàng chục Microservices ở local nhận được 3 lợi ích trực tiếp:
-
Chia sẻ tức thì mọi Commit và Branch (Shared Object Database):
-
Giả sử bạn đang đứng ở worktree
payment-hotfix, bạn vừa sửa xong bug và tạo một commit mới, hoặc bạn gõgit fetch origin. -
Ngay lập tức, ở cửa sổ VS Code bên kia đang mở
payment-service(nhánh feature), bạn đã có thể nhìn thấy commit hotfix đó đểcherry-pickhoặcrebasevào nhánh feature mà không cần phải push lên mạng rồi pull về lại. Mọi thứ nằm chung trong một khoobjects/dưới ổ cứng của bạn!
-
-
Tiết kiệm 99% dung lượng Git và tốc độ khởi tạo cực nhanh:
- Vì không phải tải lại dữ liệu từ Github/Gitlab/Bitbucket và không phải copy thư mục
.git/objects, việc tạo một worktree mới chỉ tốn thời gian hệ điều hành copy các file source code (thường mất 0.5 đến 2 giây trên ổ cứng SSD NVMe).
- Vì không phải tải lại dữ liệu từ Github/Gitlab/Bitbucket và không phải copy thư mục
-
Cơ chế khóa nhánh an toàn (Branch Checkout Lock):
-
Một câu hỏi rất hay gặp: "Điều gì xảy ra nếu cả 2 worktree cùng checkout vào chung nhánh
mainvà cùng sửa code?" -
Nếu điều đó xảy ra, 2 thư mục sẽ làm loạn lịch sử của cùng một nhánh. Vì vậy, Git thiết kế một hàng rào bảo vệ sẵn ở tầng
.git/worktrees: Một nhánh chỉ được phép mở (checkout) trên tối đa 1 Worktree tại một thời điểm. Nếu nhánhfeature/loyalty-v2đang mở ở worktree A, và bạn cố tình gõgit checkout feature/loyalty-v2ở worktree B, Git sẽ chặn lại ngay lập tức với thông báo lỗifatal: 'feature/loyalty-v2' is already checked out at .... Điều này giúp bạn không bao giờ sợ sửa nhầm hoặc ghi đè code của chính mình.
-
Tóm tắt Bài 2 & Chuẩn bị cho Bài 3
Hiểu đơn giản, Git Worktree biến thư mục .git gốc thành một Database trung tâm, còn mỗi thư mục Worktree bạn tạo ra giống như một Client kết nối vào Database đó với phiên làm việc (HEAD, index) hoàn toàn độc lập.
Khi đã nắm vững bản chất bên dưới lớp vỏ này, ở Bài 3: Các lệnh Git Worktree cốt lõi dành cho Backend Developer, chúng ta sẽ bắt tay vào thực hành trực tiếp trên Terminal bộ lệnh quản lý vòng đời của Worktree (tạo mới, liệt kê, khóa bảo vệ và dọn dẹp sạch sẽ sau khi merge PR).
All rights reserved