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 removehoặcprune.
So sánh nhanh với các phương án khác:
| Cách | Ưu | Nhược |
|---|---|---|
git stash + git checkout | Không cần thư mục khác | Dễ 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àn | Tốn gấp đôi disk, fetch nhiều lần, refs không chia sẻ |
git worktree | Share object DB, nhiều nhánh live cùng lúc | Cầ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ánhmainvà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ụ có 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:
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/fooHoặc release worktree kia (commit/stash rồi
git checkoutsang 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/checkoutlà đủ, đỡ 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ôngrm -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ể.