Git Worktree: Làm việc nhiều branch cùng lúc mà không cần stash — hoatq.dev

cat blog/.md

Git Worktree: Làm việc nhiều branch cùng lúc mà không cần stash

date: tags: git, productivity, developer-tools

Bạn đang code ngon lành trên branch feature/payment — đang sửa đến giữa file, IDE mở 10 tab, debugger bật sẵn. Rồi đột nhiên: “Ê ông ơi, review giúp cái PR gấp” hoặc “Production có bug, fix hotfix ngay nhé”.

Lúc này bạn phải git stash, switch branch, xong quay lại thì stash conflict, IDE mất hết context, debugger reset. Quen chưa?

Mình từng chịu cảnh này suốt cho đến khi phát hiện ra git worktree — một tính năng có sẵn trong Git mà ít người biết đến. Nó cho phép bạn checkout nhiều branch cùng lúc trên các thư mục khác nhau, không cần stash, không cần clone thêm repo.

Vấn đề: Context switching với git stash là ác mộng

Workflow truyền thống khi cần chuyển branch:

# Đang làm dở feature/payment...
git stash push -m "payment wip"
git checkout hotfix/login-bug
# ... fix bug, commit, push ...
git checkout feature/payment
git stash pop
# Hy vọng không conflict 🙏

Nghe thì đơn giản, nhưng thực tế:

  • Stash hay bị quêngit stash list dài dằng dặc, không nhớ cái nào là cái nào
  • Stash conflict — pop ra mà file đã thay đổi ở branch khác là đau đầu
  • Mất context — IDE phải reload, dev server restart, dependencies có thể khác nhau giữa các branch
  • Không thể chạy song song — muốn so sánh output giữa 2 branch? Phải switch qua switch lại

Nếu bạn đang dùng cách clone repo ra nhiều folder thì cũng giải quyết được, nhưng tốn disk (mỗi clone là một bản copy đầy đủ .git), và không share history.

Git Worktree là gì?

Git worktree cho phép bạn tạo nhiều working directory từ cùng một repository. Mỗi worktree là một thư mục riêng, checkout một branch riêng, nhưng share chung .git data — nghĩa là share commit history, remote, config.

Hình dung thế này:

~/projects/
├── my-app/                  # Main worktree (branch: feature/payment)
│   ├── .git/
│   ├── src/
│   └── ...
├── my-app-hotfix/           # Linked worktree (branch: hotfix/login-bug)
│   ├── .git (file, trỏ về main)
│   ├── src/
│   └── ...
└── my-app-review/           # Linked worktree (branch: pr/new-dashboard)
    ├── .git (file, trỏ về main)
    ├── src/
    └── ...

Ba thư mục, ba branch khác nhau, mở song song trên ba cửa sổ IDE. Không stash, không conflict, không mất context.

Các lệnh cơ bản

Tạo worktree mới

# Tạo worktree từ branch đã có
git worktree add ../my-app-hotfix hotfix/login-bug

# Tạo worktree + tạo branch mới luôn
git worktree add -b hotfix/urgent-fix ../my-app-hotfix main

Lệnh đầu tiên sẽ tạo thư mục ../my-app-hotfix và checkout branch hotfix/login-bug vào đó. Branch hiện tại trên main worktree không hề bị ảnh hưởng.

Xem danh sách worktree

git worktree list

Output:

/Users/hoa/projects/my-app           cb565aa [feature/payment]
/Users/hoa/projects/my-app-hotfix    a1b2c3d [hotfix/login-bug]
/Users/hoa/projects/my-app-review    d4e5f6g [pr/new-dashboard]

Xóa worktree khi xong việc

# Xóa worktree (xóa thư mục + unlink)
git worktree remove ../my-app-hotfix

# Hoặc xóa thư mục thủ công rồi dọn dẹp
rm -rf ../my-app-hotfix
git worktree prune

Use cases thực tế

1. Review PR mà không mất context

Đây là use case mình dùng nhiều nhất. Đang code dở, cần review PR của đồng nghiệp:

# Tạo worktree cho PR cần review
git fetch origin pull/42/head:pr-42
git worktree add ../my-app-review pr-42

# Mở IDE mới ở thư mục worktree
code ../my-app-review

Review xong, comment trên GitHub, rồi dọn dẹp:

git worktree remove ../my-app-review
git branch -D pr-42

Main worktree vẫn nguyên xi, dev server vẫn chạy, IDE vẫn mở đúng chỗ.

2. Hotfix production không gián đoạn feature

# Đang làm feature/payment, production cần fix gấp
git worktree add -b hotfix/critical-bug ../my-app-hotfix main

cd ../my-app-hotfix
# Fix bug, test, commit
git push origin hotfix/critical-bug

# Quay lại feature, xóa worktree
cd ../my-app
git worktree remove ../my-app-hotfix

Không stash, không cần nhớ đang làm đến đâu. Feature branch sạch sẽ y nguyên.

3. So sánh output giữa hai branch

Muốn xem behavior khác nhau giữa branch hiện tại và main? Chạy cả hai cùng lúc:

# Tạo worktree cho main
git worktree add ../my-app-main main

# Terminal 1: chạy app ở feature branch (port 3000)
cd ~/projects/my-app && npm run dev -- --port 3000

# Terminal 2: chạy app ở main (port 3001)
cd ~/projects/my-app-main && npm run dev -- --port 3001

# Mở 2 tab browser, so sánh trực tiếp

Cực kỳ hữu ích khi debug regression hoặc demo “before vs after” cho team.

4. Chạy test trên branch khác mà không block

# Tạo worktree để chạy full test suite
git worktree add ../my-app-test feature/payment

cd ../my-app-test
npm test  # Chạy test ở đây

# Trong khi đó, ở main worktree vẫn tiếp tục code bình thường

Lưu ý quan trọng

Không thể checkout cùng một branch ở hai worktree

Git không cho phép hai worktree cùng checkout một branch — vì sẽ gây xung đột khi commit:

# ❌ Sẽ báo lỗi nếu branch đã được checkout ở worktree khác
git worktree add ../another-dir feature/payment
# fatal: 'feature/payment' is already checked out at '/Users/hoa/projects/my-app'

Đây là thiết kế có chủ đích, không phải bug.

Dependencies cần install riêng

Mỗi worktree có node_modules, vendor, hay virtual env riêng. Sau khi tạo worktree, nhớ:

cd ../my-app-hotfix
npm install    # Node.js
# hoặc
composer install   # PHP/Laravel
# hoặc
pip install -r requirements.txt   # Python

Nếu dùng monorepo lớn, đây có thể tốn thời gian. Tip: dùng npm ci thay vì npm install cho nhanh hơn.

Dọn dẹp worktree thường xuyên

Worktree tạo xong quên xóa sẽ chiếm disk và làm rối git worktree list. Tạo thói quen:

# Kiểm tra worktree nào đã bị xóa thư mục nhưng chưa unlink
git worktree prune

# Xem danh sách và dọn các worktree không cần nữa
git worktree list

So sánh: Worktree vs Stash vs Clone

git stashgit clonegit worktree
Giữ context hiện tạiKhông
Tốn thêm diskKhôngNhiều (full clone)Ít (share .git)
Share history/remoteN/AKhông (riêng biệt)
Chạy song songKhông
Setup nhanhNhanhChậm (clone + install)Nhanh

Worktree gần như “best of both worlds” — giữ context như clone nhưng nhẹ và nhanh hơn nhiều.

Tips

  1. Đặt tên thư mục có quy ước — VD: {repo}-{mục đích} như my-app-review, my-app-hotfix để dễ nhận biết
  2. Tạo alias cho các lệnh hay dùng:
# Thêm vào ~/.zshrc hoặc ~/.bashrc
alias gwta='git worktree add'
alias gwtl='git worktree list'
alias gwtr='git worktree remove'
  1. Kết hợp với tmux/terminal tabs — mỗi worktree một tab, switch cực nhanh
  2. Dùng --detach nếu chỉ cần xem code mà không cần branch cụ thể:
git worktree add --detach ../my-app-browse HEAD~10

Kết

git worktree là một trong những tính năng “ẩn” của Git mà khi biết rồi thì không thể quay lại. Nó giải quyết triệt để vấn đề context switching — thứ mà mọi developer đều gặp hàng ngày nhưng ít ai nghĩ đến cách giải quyết tốt hơn git stash.

Lần tới khi cần review PR hoặc fix hotfix gấp, thay vì stash rồi cầu nguyện, hãy thử:

git worktree add ../quick-fix hotfix/bug

Mình đảm bảo bạn sẽ thấy workflow của mình smooth hơn hẳn.


Bạn đã từng dùng git worktree chưa? Hay có trick nào khác để xử lý context switching? Chia sẻ với mình qua email nhé!

// reactions



hoatq@dev : ~/blog $