Git-Worktree多分支并行开发
你正在 feature 分支改了 20 个文件,线上突然报了个紧急 bug——怎么办?
用 stash 的话,需要 5 步:暂存 → 切分支 → 修 bug → 切回来 → 还原(还要祈祷没有冲突)。Git Worktree 让你只需 2 步:新建一个目录直接开始修,feature 的代码、IDE 窗口、编译缓存全程不受干扰。
1. 快速上手:5 分钟跑通第一个 Worktree
1.1 验证环境
Worktree 是 Git 内置功能,Git 2.5+ 即可使用:
1 | $ git --version |
1.2 创建第一个工作树
进入任意 Git 仓库,执行:
1 | $ git worktree add ../my-hotfix -b hotfix/urgent |
这条命令做了两件事:在 ../my-hotfix 创建一个新目录,并在其中检出新分支 hotfix/urgent。你的当前目录(feature 分支)完全不受影响。
1.3 查看当前所有工作树
1 | $ git worktree list |
两行分别对应两个目录,对应两个分支,独立共存。
1.4 清理工作树
修完 bug 之后,删除工作树:
1 | $ git worktree remove ../my-hotfix |
再次 list,只剩主工作树:
1 | $ git worktree list |
跑通了基本流程,再来看 Worktree 和分支切换的本质区别。
2. Worktree Vs Branch:为什么不用 Stash + checkout?
2.1 本质区别
Branch 是指针,Worktree 是工作空间。
- Branch:一个指向某个 commit 的引用,本身不包含任何文件。
git branch feature只是在.git/refs/heads/下创建一个 41 字节的文本文件。 - Worktree:一个完整的工作目录,包含独立的
HEAD、index(暂存区)和所有工作文件。
类比:Branch 是书的目录里的页码,Worktree 是你打开的那本书。你可以有 100 个页码,但书一次只能翻到一页——除非用 worktree “ 复印 “ 出另一本,同时翻到不同页。
2.2 对比表
| 维度 | 只用 Branch 切换 | 用 Worktree |
|---|---|---|
| 并行编辑 | ❌ 同一时刻只能在一个分支上工作 | ✅ 多个目录同时打开不同分支 |
| 上下文保留 | ❌ 切换前必须 stash 或 commit | ✅ 每个目录独立保留修改状态 |
| 编译缓存 | ❌ 切换后 node_modules、build 产物可能失效 | ✅ 各目录有独立的构建产物 |
| IDE 状态 | ❌ 切换后需要重新打开文件、恢复断点 | ✅ 每个目录可以用独立的 IDE 窗口 |
| 操作步骤 | 多(stash → checkout → work → checkout → pop) | 少(直接进入另一个目录) |
| 磁盘开销 | 无额外开销 | 每个工作树占用一份工作文件空间(共享 .git) |
3. 核心命令详解
3.1 Git Worktree Add —— 创建工作树
检出已有分支:
1 | $ git worktree add ../hotfix-dir hotfix/login-bug |
创建新分支并检出:
1 | $ git worktree add ../experiment -b feature/try-new-api |
可选参数
参数 说明 -b <名称>创建并检出一个新分支 --lock创建后自动加锁,防止被 prune误清理(Git 2.15+)
3.2 Git Worktree List —— 查看所有工作树
1 | $ git worktree list |
每行显示:目录路径、当前 commit hash、检出的分支名。
可选参数:
--porcelain输出机器可读格式,适合脚本解析。
3.3 Git Worktree Remove —— 删除工作树
正确删除方式(同时删除目录和 .git/worktrees 中的记录):
1 | $ git worktree remove ../hotfix-dir |
删除前工作树必须干净(无未提交修改)。如果想强制删除:
1 | $ git worktree remove --force ../experiment |
不要用
rm -rf直接删目录,会在.git/worktrees/中留下残留记录。
3.4 Git Worktree Prune —— 清理残留记录
用于清理 .git/worktrees/ 中指向已不存在目录的过期记录(通常在误用 rm -rf 之后):
1 | $ git worktree prune --dry-run # 先预览,不实际执行 |
3.5 命令与架构总览
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#3B82F6', 'primaryTextColor': '#1E3A5F', 'primaryBorderColor': '#2563EB', 'lineColor': '#60A5FA', 'secondaryColor': '#10B981', 'tertiaryColor': '#F59E0B'}}}%%
flowchart TB
subgraph repo ["📦 Git 仓库(共享 .git)"]
direction TB
git_dir[".git 目录\n(对象库/引用/配置)"]
end
subgraph worktrees ["🗂️ 工作目录们"]
direction LR
main["📁 main-project/\n(主工作树 - main 分支)"]
wt1["📁 hotfix-dir/\n(附加工作树 - hotfix 分支)"]
wt2["📁 feature-dir/\n(附加工作树 - feature 分支)"]
end
git_dir -->|"共享"| main
git_dir -->|"共享"| wt1
git_dir -->|"共享"| wt2
subgraph commands ["⚙️ 核心命令"]
add["git worktree add"]
list["git worktree list"]
remove["git worktree remove"]
prune["git worktree prune"]
end
add -->|"创建"| wt1
add -->|"创建"| wt2
remove -->|"删除"| wt1
list -->|"查看"| worktrees
classDef primary fill:#3B82F6,stroke:#2563EB,color:#fff
classDef success fill:#10B981,stroke:#059669,color:#fff
classDef info fill:#0EA5E9,stroke:#0284C7,color:#fff
class git_dir primary
class main,wt1,wt2 success
class add,list,remove,prune info4. 错误处理
新手使用 worktree 时,有 4 类高频报错,每类给出报错现象、原因和修复方法。
4.1 Already Checked Out
现象: 尝试在新工作树检出一个已有工作树正在使用的分支:
1 | $ git worktree add ../another-dir feature/login |
原因: 同一个分支不能同时被两个工作树检出,否则两边的 HEAD 会冲突。
修复: 换一个新分支名,在已有工作树中操作,或者 cd 到已有工作树直接开始:
1 | $ git worktree add ../another-dir -b feature/login-v2 |
4.2 Rm -rf 误删后修复
现象: 手动删除了工作树目录后,再操作该分支报错:
1 | $ rm -rf ../hotfix-dir # 误操作 |
原因: .git/worktrees/hotfix-dir/ 中的记录仍然存在,Git 认为该分支还在使用中。
修复: 用 prune 清理残留记录:
1 | $ git worktree prune |
4.3 在工作树内切换分支
现象: 在附加工作树目录里执行 git checkout,意外切换到了其他分支:
1 | # 当前在 ../hotfix-dir(hotfix/urgent 分支) |
原因: 这会破坏隔离性——如果 main 已经在主工作树中被检出,两个工作树同时持有同一分支,状态会混乱。
修复: 立即 checkout 回原分支:
1 | $ git checkout hotfix/urgent |
之后如果需要操作另一个分支,应该回到主工作树中用 git worktree add 创建新的工作树,而不是在已有工作树内切换。
4.4 工作树内有未提交修改
现象: 删除工作树时提示有修改未提交:
1 | $ git worktree remove ../hotfix-dir |
原因: worktree remove 的默认行为是安全删除,发现未保存的工作会拦截。
修复(两种方式选一):
1 | # 方式一:先保存工作再删除(推荐) |
5. 进阶实践
5.1 方案横向对比
| 维度 | git worktree | git clone(多份克隆) | git stash + checkout |
|---|---|---|---|
| 磁盘占用 | 低(共享对象库) | 高(完整复制) | 无额外占用 |
| 并行工作 | ✅ 可同时编辑多分支 | ✅ 可以 | ❌ 只能一个分支 |
| 上下文保留 | ✅ 每个目录独立状态 | ✅ 各自独立 | ❌ stash 容易混乱 |
| 同步性 | 即时共享 commit | 需要 push/pull | 不涉及 |
| 编译缓存 | ✅ 各目录独立缓存 | ✅ 各目录独立 | ❌ 切换后缓存失效 |
5.2 裸仓库 + Worktree 模式
适合需要频繁并行维护多个长期分支的场景:
1 | # 克隆为裸仓库,没有工作目录 |
推荐的目录结构:
1 | ~/projects/my-repo.git/ ← 裸仓库(.git 本身) |
所有分支地位平等,没有 “ 主工作树 “ 的心智负担。
6. 动手练习
5 分钟体验 worktree 并行开发:
1 | # 1. 进入任意 Git 仓库 |
验收标准:步骤 3 显示 2 个工作树路径,步骤 6 只剩 1 个。
7. 深度探索
7.1 内部实现
每个附加工作树在 .git/worktrees/<name>/ 下有自己的 HEAD、index、MERGE_HEAD 等文件。工作树目录中的 .git 不是一个目录,而是一个文本文件,内容为:
1 | gitdir: /path/to/.git/worktrees/<name> |
这是一个指回主仓库的指针,所有工作树共享同一份对象库,不会重复存储 commit 历史。
Git 2.15+ 支持 --lock 参数,防止工作树被 prune 误删,适合放在移动硬盘或网络挂载位置:
1 | $ git worktree add --lock /mnt/usb/review review-branch |
7.2 真实案例
- Linux 内核开发:维护者同时审查多个补丁系列,用 worktree 为每个补丁系列创建独立的测试环境
- 大型前端项目(如 React):一个 worktree 跑
main分支的 dev server 做回归测试,另一个 worktree 开发新 feature,两个浏览器窗口同时对比 - CI/CD 流水线:构建服务器用裸仓库 + worktree 并行编译多个分支,共享对象库减少
fetch时间和磁盘占用