Dành cho ai: lập trình viên dùng Git hằng ngày, đang làm feature dài nhưng vẫn phải nhảy qua nhánh khác (hotfix, review PR, demo bản cũ) mà không muốn stash + checkout liên tục; hoặc muốn chạy hai build song song (ví dụ test A/B, compare behavior) trên cùng một codebase.

Sau bài này bạn sẽ nắm được: worktree thật sự là gì, khi nào nó giúp vs khi nào thêm rối, pattern bố trí thư mục, cách xử lý khi worktree “mắc kẹt”, và cách dạy IDE / CI chạy với worktree.


1. Worktree thực sự là gì?

Trong Git, một working tree là thư mục có file mã nguồn đang được checkout của một nhánh. Mặc định khi bạn git clone, bạn có một working tree gắn với một thư mục .git chứa object database (blob, tree, commit, pack), refs (nhánh, tag) và config.

git worktree cho phép bạn có nhiều working tree cùng dùng chung một object database. Nghĩa là:

  • Mỗi worktree là một thư mục độc lập trên đĩa, có file nguồn tương ứng với một commit/branch riêng.
  • Nhưng lịch sử commit, remote, stash (phần cơ bản) vẫn chia sẻ — không phải bạn có hai “clone” tách biệt.
  • Một worktree là primary (thường là thư mục chứa .git/), các worktree phụ gọi là linked worktree, mỗi cái có metadata riêng trong .git/worktrees/<name>/.

Hệ quả quan trọng:

  • Bạn không phải fetch/pull hai lần cho hai nơi: một lần ở bất kỳ worktree nào là cả hệ thấy.
  • Nhưng bạn không thể checkout cùng một nhánh local ở hai worktree cùng lúc — Git chặn để tránh xung đột working copy.
  • Xoá thư mục worktree không tự làm sạch metadata; cần git worktree remove hoặc prune.

So sánh nhanh với các phương án khác:

CáchƯuNhược
git stash + git checkoutKhông cần thư mục khácDễ mất context, build/test có thể bị incremental sai, mất thời gian stash/unstash
Clone nhiều lần (git clone <repo> other-dir)Độc lập hoàn toànTốn gấp đôi disk, fetch nhiều lần, refs không chia sẻ
git worktreeShare object DB, nhiều nhánh live cùng lúcCần kỷ luật đặt thư mục, có vài cạm bẫy (xem bên dưới)

2. Lệnh cơ bản

2.1. Liệt kê worktree

git worktree list
# /home/me/proj            abc1234 [feature/api]
# /home/me/proj-hotfix     def5678 [main]
# /home/me/proj-review     9876abc (detached HEAD)

2.2. Thêm worktree cho nhánh có sẵn

git worktree add ../proj-hotfix main
  • Git tạo thư mục ../proj-hotfix, checkout nhánh main vào đó.
  • Thư mục gốc (primary) không đổi trạng thái.

2.3. Tạo nhánh mới kèm worktree

git worktree add -b fix/login-500 ../proj-fix main

Tương đương: tạo nhánh fix/login-500 từ main, checkout vào thư mục ../proj-fix.

2.4. Worktree ở commit/tag (detached)

git worktree add --detach ../proj-v1.8.0 v1.8.0

Dùng khi muốn chỉ đọc một tag (ví dụ để build bản release cũ, so sánh file).

2.5. Xoá worktree

git worktree remove ../proj-hotfix
# Nếu còn uncommitted changes, Git sẽ báo; thêm --force để ép:
git worktree remove --force ../proj-hotfix

2.6. Dọn rác khi thư mục bị xoá tay

git worktree prune
# Xoá các entry trong .git/worktrees/ trỏ tới thư mục không còn tồn tại

prune an toàn (chỉ xoá metadata mồ côi), có thể chạy định kỳ hoặc trong git gc.


3. Bố trí thư mục — vài pattern

3.1. Pattern “sibling”

~/work/
├── proj/              ← worktree chính (main dev)
├── proj-hotfix/       ← worktree cho main (hotfix nhanh)
├── proj-review/       ← worktree để review PR
└── proj-benchmark/    ← worktree để chạy bench bản cũ

Ưu: dễ cd, IDE thấy ngay; mỗi thư mục một workspace. Nhược: dễ loạn nếu bạn làm nhiều repo → đặt dưới ~/work/<repo>/worktrees/ chẳng hạn.

3.2. Pattern “subdirectory” (không khuyến nghị)

~/work/proj/
├── .git/
├── src/
└── worktrees/
    ├── hotfix/
    └── review/

Nghe gọn nhưng một số tool đệ quy quét thư mục (formatter, linter) sẽ đi vào hai lần. Tránh nếu có thể.

3.3. Tên worktree

Đặt tên mô tả mục đích, không phải tên nhánh:

  • proj-hotfix (bạn có thể đổi nhánh trong đó sau này)
  • proj-review (dùng cho nhiều PR khác nhau)

Tránh proj-feature-x-abc nếu nhánh có thể đổi — metadata worktree không gắn chặt với tên nhánh, bạn có thể git checkout <branch> trong worktree đó (miễn nhánh chưa checkout ở nơi khác).


4. Các kịch bản thực tế

4.1. Hotfix ngay khi đang dở feature

Tình huống: bạn đang dở feature/cart-refactor (có build dev chạy, một số file chưa commit). Sếp ping: “Prod lỗi 500 ở /api/login, fix nhanh.”

Không cần stash:

cd ~/work/proj
git worktree add -b hotfix/login-500 ../proj-hotfix origin/main
cd ../proj-hotfix
# sửa, test, PR, merge; hotfix xong
cd ~/work/proj   # quay lại, mọi state feature còn nguyên

Điều giá trị: build cache, node_modules, test runner state trong ~/work/proj không bị reset. Với project lớn (mono-repo, Gradle, webpack), tiết kiệm vài phút mỗi lần chuyển.

4.2. Review PR mà không làm loạn môi trường

git fetch origin pull/1234/head:pr-1234
git worktree add ../proj-pr-1234 pr-1234
cd ../proj-pr-1234
# cài dependencies, chạy test

Xong thì:

cd ~/work/proj
git worktree remove ../proj-pr-1234
git branch -D pr-1234   # nếu không cần giữ ref

4.3. So sánh hiệu năng hai branch

Chạy benchmark cùng lúc trên hai worktree (hai port khác nhau) để giảm biến thiên thời gian. Hoặc chạy tuần tự nhưng giữ state warm cache ở cả hai thư mục.

4.4. Bisect trong khi vẫn làm việc

git bisect vốn thay đổi HEAD của working tree hiện tại. Nếu bạn đang dở một thay đổi lớn, tạo worktree phụ riêng để bisect:

git worktree add ../proj-bisect <some-branch>
cd ../proj-bisect
git bisect start
git bisect bad HEAD
git bisect good v1.0.0
# lặp đến khi tìm ra commit xấu
git bisect reset

Worktree chính không bị đụng vào.


5. Tương tác với phần còn lại của hệ thống

5.1. IDE / editor

Hầu hết IDE hiện đại (VSCode, JetBrains) coi worktree như repo riêng khi bạn mở thư mục đó:

  • Git panel hoạt động bình thường (commit, branch list).
  • Một số chức năng chia sẻ giữa các worktree (như thông tin refs) cần IDE refresh.
  • Không nên mở cùng một worktree trong hai cửa sổ IDE → lock file conflict.

5.2. CI cục bộ

Nếu chạy CI local (act, container runner), mỗi worktree nên có cache riêng (thư mục .ci/ hay ./.cache/) để tránh đè nhau. Tránh share node_modules symlink giữa hai worktree — dễ race condition khi cài package.

5.3. Submodule

Worktree phụ working copy submodule riêng, nhưng .gitmodules và object của submodule vẫn nằm trong primary (với Git mới, submodule dùng .git/modules/ chung). Khi làm việc với submodule trong worktree phụ:

cd ../proj-hotfix
git submodule update --init --recursive

Nhớ rằng git submodule deinit trong một worktree có thể ảnh hưởng các worktree khác nếu cấu hình share — đọc kỹ tài liệu submodule nếu dự án phụ thuộc nhiều.

5.4. Hooks

.git/hooks/ nằm ở primary. Worktree phụ dùng chung hooks (trừ khi bạn đặt core.hooksPath khác). Nghĩa là pre-commit, pre-push chạy đồng nhất — thường là điều tốt.

5.5. git rebase, git merge, git pull

Các thao tác này chạy trên worktree hiện tại, không ảnh hưởng nhánh đang checkout ở worktree khác — miễn bạn tuân quy tắc “một nhánh một worktree”.

Chú ý: git push luôn push ref; có thể push nhánh đang checkout ở worktree khác (Git không chặn push ref). Cẩn thận khi push force.


6. Các rắc rối hay gặp

6.1. “fatal: … is already checked out at …”

Bạn thử git worktree add ../proj-x feature/foo nhưng feature/foo đang được checkout ở worktree khác. Hai lựa chọn:

  1. Chuyển sang tên nhánh khác (tạo nhánh tạm):

    git worktree add -b feature/foo-review ../proj-x feature/foo
    
  2. Hoặc release worktree kia (commit/stash rồi git checkout sang nhánh khác, hoặc remove nó).

6.2. Worktree “ma” sau khi xoá thư mục tay

Bạn xoá ../proj-hotfix bằng rm -rf. Git vẫn coi nó tồn tại trong metadata:

git worktree list
# /home/me/proj-hotfix  (prunable)
git worktree prune

6.3. Branch xoá rồi vẫn còn ref

git worktree remove không tự xoá branch. Nếu muốn:

git worktree remove ../proj-fix
git branch -d fix/login-500   # -D nếu chưa merge

6.4. bare repo + worktree

Một pattern advanced: dùng bare clone làm “gốc refs” và mỗi nhánh là một worktree:

git clone --bare [email protected]:org/proj.git ~/work/proj.git
cd ~/work/proj.git
git worktree add ../proj-main main
git worktree add ../proj-develop develop

Tránh nhầm lẫn thư mục .git/ primary; nhưng rất gọn cho “một repo nhiều nhánh luôn bật”.

6.5. Worktree trên filesystem khác

Nếu worktree phụ đặt trên filesystem khác (ví dụ disk ngoài), Git vẫn hoạt động nhưng một số tool dựa vào hardlink (pack files) có thể fall back sang copy — performance thấp hơn.


7. Khi nào không nên dùng worktree

  • Dự án nhỏ, hiếm khi nhảy nhánh — stash/checkout là đủ, đỡ loạn thư mục.
  • Bạn chưa quen git: worktree thêm mental model; nên nắm vững checkout/branch/stash trước.
  • Môi trường CI/remote dev: nếu mỗi task chạy container riêng, worktree không mang lại giá trị.
  • Repo có tooling giả định một thư mục (ví dụ: một số script build cứng đường dẫn tuyệt đối).

8. Tóm tắt

  • Worktree = thêm working copy chia sẻ object database; không phải clone.
  • Giá trị chính: giữ state (build cache, test, env) khi phải làm song song; tránh stash/checkout liên tục; review PR cô lập.
  • Luật: một nhánh một worktree cùng lúc; xoá worktree bằng git worktree remove, không rm -rf.
  • Thói quen: đặt worktree ở thư mục sibling có tên mô tả, không nhét vào bên trong repo chính.
  • Debug: git worktree list, git worktree prune, kiểm tra .git/worktrees/ khi lỗi khó hiểu.

Worktree không thay thế kỷ luật branch — nó chỉ giảm ma sát khi bạn cần switch giữa nhiều dòng công việc. Dùng đúng chỗ, bạn tiết kiệm vài phút mỗi lần context switch, và qua vài tháng thành con số đáng kể.