+1

Git Worktree vào hệ sinh thái Microservices Bài 4: Chiến lược tổ chức thư mục (Directory Layout) chuẩn chỉnh với Bare Repository

Ở Bài 3, chúng ta đã làm quen với cú pháp git worktree add ../payment-hotfix. Cách dùng đường dẫn tương đối (../) nhảy ra ngoài thư mục gốc rất tiện khi bạn chỉ có 1 hoặc 2 dự án. Nhưng hãy thử nhân quy mô đó lên một hệ thống Microservices có 15 services: user, order, payment, inventory, notification...

Nếu mỗi service bạn tách ra 3 worktree (1 cho main, 1 cho feature, 1 cho hotfix) và đặt tất cả nằm ngang hàng nhau trong thư mục workspace/, bạn sẽ nhận được một "bãi rác" đúng nghĩa với 45 thư mục con trộn lẫn vào nhau. Không chỉ gây rối mắt khi tìm kiếm, cấu trúc này còn làm cho việc viết script tự động hóa hay cấu hình IDE trở nên cực kỳ phức tạp.

Để giải quyết triệt để vấn đề này, các kỹ sư hệ thống sử dụng một chiến lược quy hoạch thư mục rất tinh gọn: Kết hợp Git Worktree với Bare Repository (git clone --bare).

1. Hai sai lầm phổ biến khi tổ chức thư mục Worktree theo cách thông thường

Khi chưa biết đến Bare Repository, lập trình viên thường chọn một trong hai cách bố trí sau, và cả hai đều bộc lộ nhược điểm lớn:

Cách bố trí 1: "Tràn lan đại hải" (Sibling Directories)

Đặt mọi worktree nằm ngang hàng với thư mục gốc.

Plaintext

workspace/
├── order-service/                (Repo gốc - đang ở nhánh main)
├── order-service-feature-a/      (Worktree 1)
├── order-service-hotfix/         (Worktree 2)
├── payment-service/              (Repo gốc - đang ở nhánh develop)
├── payment-service-feature-a/    (Worktree 1)
└── payment-service-review-pr/    (Worktree 2)

  • Nhược điểm: Mất tính đóng gói (Encapsulation). Các thành phần của cùng một service bị xé lẻ và trộn lẫn với các service khác ở tầng ngoài cùng.

Cách bố trí 2: Nhét Worktree con vào bên trong Repo gốc (Nested Directories)

Để gom nhóm, nhiều người cố tình tạo thư mục worktree nằm bên trong thư mục code gốc:

Bash

# Đang đứng ở payment-service (nhánh main)
git worktree add ./hotfix-branch hotfix/payment-timeout

  • Nhược điểm chí mạng: Thư mục hotfix-branch giờ đây nằm lọt thỏm bên trong source code của nhánh main.

    • Git ở nhánh main sẽ lập tức báo Untracked files: hotfix-branch/, buộc bạn phải sửa file .gitignore.

    • Tệ hơn, các công cụ như VS Code Search, ESLint, PHPStan, Composer autoload hay Go compiler khi quét thư mục gốc sẽ quét luôn cả code bên trong thư mục hotfix-branch, dẫn đến lỗi trùng lặp class/function (Duplicate symbol) và làm treo IDE!

2. Giải pháp chuẩn: Kiến trúc "Hộp chứa" với git clone --bare

Ý tưởng cốt lõi của kiến trúc này là: Tạo một thư mục đóng vai trò là "Hộp chứa" (Container Directory) cho từng Microservice. Bên trong hộp chứa đó chỉ đặt kho dữ liệu .git (dưới dạng Bare Repo) và các thư mục Worktree ngang hàng nhau. Không có bất kỳ thư mục nào là "thư mục chính" chứa code hỗn tạp ở ngoài cùng.

Khi áp dụng mô hình này, cấu trúc của payment-service và order-service sẽ đẹp và chuẩn chỉnh như sau:

Plaintext

workspace/
├── payment-service/                <-- HỘP CHỨA CỦA PAYMENT SERVICE
│   ├── .bare/                      <-- Kho dữ liệu Git trung tâm (Bare Repo)
│   ├── .git                        <-- File trỏ vào ./.bare
│   ├── main/                       <-- [Worktree 1] Luôn ở nhánh main sạch sẽ
│   ├── feature-loyalty-v2/         <-- [Worktree 2] Đang code dở tính năng
│   └── hotfix-timeout/             <-- [Worktree 3] Xử lý lỗi khẩn cấp
│
└── order-service/                  <-- HỘP CHỨA CỦA ORDER SERVICE
    ├── .bare/
    ├── .git
    ├── main/
    └── feature-loyalty-v2/

Tại sao cấu trúc này lại hoàn hảo cho Microservices?

  1. Cô lập tuyệt đối: Khi bạn mở IDE vào payment-service/main, nó chỉ chứa đúng source code của main. Khi mở vào payment-service/hotfix-timeout, nó chỉ chứa đúng code của hotfix. Không bao giờ có chuyện IDE hay trình biên dịch quét nhầm sang nhánh khác.

  2. Đồng nhất vai trò: Mọi nhánh (main, feature, hotfix) đều bình đẳng như nhau dưới dạng các thư mục con độc lập.

  3. Thư mục workspace/ cực kỳ gọn gàng: Dù bạn tạo bao nhiêu nhánh con, nhìn từ ngoài workspace/, bạn vẫn chỉ thấy đúng danh sách các Microservices của hệ thống (payment-service, order-service...).

3. Hướng dẫn thiết lập Bare Repo + Worktree từng bước (Chuẩn thực chiến)

Giả sử bạn bắt đầu thiết lập dự án payment-service trên máy theo mô hình chuẩn này. Hãy thực hiện đúng 4 bước dưới đây (mất chưa tới 1 phút):

**1.Tạo thư mục hộp chứa và Clone dưới dạng Bare:**Bước 1.

Thay vì git clone thông thường, chúng ta tạo thư mục chứa trước và clone toàn bộ ruột của .git vào một thư mục ẩn tên là .bare:

Bash

mkdir payment-service
cd payment-service

# Clone dữ liệu Git vào thư mục .bare (không bung source code ra ngoài)
git clone --bare git@github.com:company/payment-service.git .bare

Kiểm tra: Lúc này nếu gõ ls -la, bạn chỉ thấy duy nhất thư mục .bare, hoàn toàn chưa có file code nào gây vướng víu.

**2.Tạo file .git chỉ đường ở thư mục hộp chứa:**Bước 2.

Để bạn có thể đứng ngay tại thư mục payment-service/ và gõ các lệnh git worktree ... mà không cần phải cd vào trong .bare, hãy tạo một file .git trỏ thẳng vào .bare:

Bash

echo "gitdir: ./.bare" > .git

Kiểm tra: Gõ thử git branch hoặc git worktree list, Git sẽ nhận diện ngay lập tức kho dữ liệu bên trong .bare.

**3.Sửa cấu hình Fetch cho Bare Repository:**Bước 3 - Cực kỳ quan trọng.

Mặc định của Git khi dùng --bare (vốn thiết kế cho Git Server) là nó không tự động ánh xạ các nhánh từ origin/* về refs/remotes/origin/* khi bạn gõ git fetch. Điều này sẽ khiến bạn không thấy các nhánh mới của đồng nghiệp trên remote.

Hãy chạy đúng 1 dòng lệnh cấu hình này để khắc phục vĩnh viễn:

Bash

git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"

# Chạy fetch lần đầu để kéo toàn bộ danh sách nhánh remote về
git fetch origin

Kiểm tra: Gõ git branch -r, nếu bạn thấy danh sách origin/main, origin/develop... hiện ra đầy đủ là thành công!

**4.Khởi tạo các không gian làm việc (Worktrees)😗*Bước 4.

Giờ đây, hộp chứa payment-service đã sẵn sàng. Bạn có thể tạo ra các worktree con vô cùng ngăn nắp ngay bên trong nó:

Bash

# 1. Tạo thư mục cho nhánh main (luôn giữ sạch để đối chiếu hoặc pull code mới)
git worktree add main main

# 2. Tạo thư mục để code tính năng mới từ origin/main
git worktree add -b feature/loyalty-v2 feature-loyalty origin/main

# 3. Tạo thư mục để sửa hotfix
git worktree add -b hotfix/payment-stuck hotfix-stuck origin/main

Kiểm tra: Gõ git worktree list, bạn sẽ thấy các không gian làm việc được xếp lớp gọn gàng bên trong payment-service/.

4. Mẹo tự động hóa: Biến 4 bước trên thành 1 câu lệnh duy nhất

Nếu hệ thống của bạn có 20 Microservices, việc gõ lặp đi lặp lại 4 bước trên cho từng repo sẽ rất mỏi tay. Bạn có thể mở file cấu hình Terminal (~/.bashrc hoặc ~/.zshrc) và thêm hàm git-clone-bare nhỏ gọn này vào:

Bash

# Hàm tự động clone dự án theo chuẩn Bare Repo + Worktree
function gcb() {
    local url=$1
    # Tự động lấy tên repo từ URL (hoặc nhận tên thư mục từ tham số thứ 2)
    local basename=$(basename "$url" .git)
    local dirname=${2:-$basename}

    mkdir -p "$dirname"
    cd "$dirname" || return

    # Bước 1 & 2: Clone bare và tạo file con trỏ .git
    git clone --bare "$url" .bare
    echo "gitdir: ./.bare" > .git

    # Bước 3: Cấu hình fetch remote branches chuẩn chỉnh
    git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"
    git fetch origin

    echo "✅ Đã khởi tạo Bare Repo thành công tại: $(pwd)"
    echo "👉 Hãy dùng 'git worktree add <thư-mục> <nhánh>' để bắt đầu code!"
}

Sau khi lưu lại (source ~/.zshrc), mỗi khi nhận một Microservice mới từ công ty, bạn chỉ cần gõ đúng 1 dòng:

Bash

gcb git@github.com:company/inventory-service.git

Mọi cấu hình .bare, .git và remote.origin.fetch đều được thiết lập hoàn hảo trong tích tắc!

Tóm tắt Chương 1 & Mở đầu Chương 2

Vậy là chúng ta đã hoàn thành Chương 1 (Từ Bài 1 đến Bài 4). Bạn đã nắm được từ lý do ra đời, kiến trúc lõi bên trong .git, bộ lệnh thao tác cho đến cách quy hoạch cây thư mục chuẩn chỉnh bằng Bare Repository.

Tuy nhiên, trong thực tế kiến trúc Microservices có hai trường phái tổ chức mã nguồn rất khác nhau:

  • Monorepo: Toàn bộ 20 Microservices nằm chung trong 1 Git Repository khổng lồ.

  • Polyrepo (Multi-repo): Mỗi Microservice là 1 Git Repository riêng biệt.

Làm thế nào để áp dụng mô hình Worktree vừa học vào từng trường phái này mà không bị rối khi một tính năng đòi hỏi phải sửa code ở nhiều services cùng lúc? Hẹn gặp lại bạn ở Chương 2 — Bài 5: Áp dụng Git Worktree cho Microservices dạng Monorepo!


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí