🌳 Git Worktree 完整指南

Worktree 让同一个仓库同时拥有多个工作目录,每个目录独立 checkout 不同分支,但共享一个 .git 对象库。用来并行处理多个分支,不必反复 stash 切换。

🎯 解决什么问题

同时推进多条线时的经典困境:

❌ 传统做法
- 在 feature-a 上开发到一半
- 要切到 main 修个 bug → 先 git stash
- 切过去,改完,切回来,git stash pop
- 忘了 stash 什么 → 冲突、丢改动

✅ Worktree
- ~/projects/repo/          → main 分支,修 bug
- ~/projects/repo-hotfix/   → feature-a 分支,继续开发
- ~/projects/repo-refactor/ → feature-b 分支,第三条线
三个目录并存,互不干扰

核心价值:省掉 stash/切换的成本,同时避免「切分支时工作区不干净」的错误。


🛠️ 基本操作

创建 worktree

# 从当前 HEAD 创建新分支
git worktree add ../repo-hotfix -b hotfix/login-crash
 
# 为已存在的分支创建
git worktree add ../repo-feature-a feature-a
 
# 基于远程分支创建
git worktree add --track -b feature-x ../repo-x origin/feature-x

日常使用

# 列出所有 worktree
git worktree list
 
# 删除 worktree(必须先删目录)
git worktree remove ../repo-hotfix
 
# 强制删除(有未提交改动时会拒绝)
git worktree remove --force ../repo-hotfix
 
# 清理失效记录(目录已被手动 rm 掉)
git worktree prune

📂 目录结构

每个 worktree 的目录内容与普通 clone 一致,但 .git 是文件不是目录,里面指向主仓库:

# 某个 worktree 里的 .git 文件内容
$ cat ../repo-hotfix/.git
gitdir: /Users/me/projects/repo/.git/worktrees/repo-hotfix

主仓库的 .git/worktrees/ 下存放所有 worktree 的元数据:

.git/
├── objects/          # 所有对象,共享
├── refs/             # 所有分支,共享
├── worktrees/
│   ├── repo-hotfix/  # 每个 worktree 的独立 HEAD 和 index
│   └── repo-refactor/
└── HEAD              # 主 worktree 的 HEAD

关键理解:worktree 共享 objects 和 refs(所以省空间、克隆瞬间完成),但每个 worktree 有独立的 HEAD 和 index(所以工作区互不干扰)。


⚠️ 常见坑

1. 同一分支不能在两个 worktree 同时 checkout

症状:

fatal: 'feature-a' is already used by worktree at '/path/to/repo-a'

原因:git 强制一个分支只能有一个工作区。这是设计限制,不是 bug。

解决:用 detached HEAD 绕过:

git worktree add --detach ../repo-preview feature-a

此时目录里是 feature-a 的代码,但 HEAD 游离,不受分支保护。

2. 主仓库不是裸仓库时,git worktree add 源目录要明确

在已经是 worktree 的目录里执行,会套娃:

# 错误示范:在 ~/repo-a 里执行
cd ~/repo-a
git worktree add ../repo-b -b feature-b
# repo-b 会基于 repo-a 的当前 HEAD,不是主仓库

解决:用绝对路径或从主仓库目录执行。

3. 分支删了但 worktree 还在

git branch -d feature-a 会失败:

error: Cannot delete branch 'feature-a' checked out at '/path/to/repo-a'

解决:先删 worktree,再删分支:

git worktree remove ../repo-a
git branch -d feature-a

4. node_modules 不共享,每次都要重装

Node 项目里切 worktree 后 node_modules 不存在(它被 gitignore,不会被 checkout)。

解决:用软链接或包管理器的 workspace 能力:

ln -s ../repo/node_modules ./node_modules

更稳的做法是用 pnpm/npm 的全局 store,硬链接会很快。

5. 磁盘上的 .env、本地配置不在

.env、.env.local 通常 gitignore,新 worktree 里没有,跑不起来。

解决:创建后手动复制,或用脚本自动化:

cp ~/repo/.env ../repo-b/.env

6. 忘记 git worktree prune

手动 rm -rf 删了 worktree 目录后,git 里还留着记录,列表里显示为 prunable:

git worktree list          # 看到 prunable
git worktree prune         # 清理

7. worktree 目录放在仓库内部

~/repo/
├── .git/
├── src/
└── feature-a/          ← worktree 在仓库内部

症状:feature-a 里的文件被仓库当成普通文件追踪,且可能无限递归。

解决:用 ../ 或绝对路径,放到仓库外面。


🔄 与 stash / clone 的对比

方案额外磁盘切换成本适合
git stash极小高(易忘、易冲突)临时切分支看一眼
worktree每个一份工作区(共享 objects)零并行推进多分支
git clone完整副本(含 objects)中不同仓库

选择逻辑:

  • 同一仓库不同分支并行 → worktree
  • 不同仓库 → clone
  • 临时看一眼别的分支 → stash 或 git show 就够

💡 实用组合

配合「一个功能一个分支」工作流

# 主目录保持干净,随时能跑主干
cd ~/repo
 
# 每个功能开一个 worktree
git worktree add ../repo-feat-login  -b feat/login
git worktree add ../repo-feat-pay   -b feat/pay
 
# 分别进目录开发
cd ~/repo-feat-login  && npm run dev   # :3001
cd ~/repo-feat-pay   && npm run dev   # :3002
# 两个 dev server 并行,互不干扰
 
# 完成后清理
git worktree remove ~/repo-feat-login

配合 PR review

# 检出 PR 分支来 review,不影响自己正在写的代码
git worktree add --track -b pr-1234 ~/repo-review origin/pr-1234
cd ~/repo-review && npm test
# review 完
git worktree remove ~/repo-review

只看某分支的文件,不切换

# 极端情况:只想读某分支某个文件
git show feature-a:src/index.ts

🔧 常用命令速查

# 创建
git worktree add <path>                     # 新建分支
git worktree add <path> -b <branch>         # 指定分支名
git worktree add <path> <existing-branch>   # 用已有分支
git worktree add --detach <path> <commit>   # detached HEAD
git worktree add --track -b <branch> <path> <remote>/<branch>
 
# 查看
git worktree list                           # 列出所有
git worktree list --porcelain               # 机器可读格式
 
# 删除
git worktree remove <path>
git worktree remove --force <path>          # 有改动时强制
git worktree prune                          # 清理失效记录
 
# 诊断
git worktree list --porcelain | grep prunable

🛡️ 使用建议

  • worktree 目录放仓库外面,用 ../repo-feature 这种平行结构
  • 命名规范:目录名带分支名(repo-hotfix 对应 hotfix/xxx),避免自己都分不清
  • Node 项目注意依赖和 .env,新 worktree 后先补齐再跑
  • 定期 git worktree prune,尤其手动删过目录之后
  • CI 上不适用,worktree 是本地开发工具

🔗 相关


最后更新:2026-10-05