一句话理解
一个 Git 仓库,同时在多个目录里检出不同分支,共享同一份 .git 数据库。
无需 clone 多份仓库,就能并行工作在多个分支上。
先搞清楚两个最基础的问题
💡 Q1:这几个文件夹是独立的吗?
→ 是的,它们就是磁盘上 3 个完全独立的文件夹,跟普通文件夹一模一样。
Q2:它们彼此有什么关联?
→ 共享一份隐藏的"历史档案库"(.git),但你肉眼看不到这层关联。
用真实文件演示:一步一步看
假设有一个项目 myapp,就两个文件:README.md 和 main.c。
第 1 步:原始状态,只有 1 个文件夹
plaintext
/home/mi/myapp/ ← 唯一的文件夹(主 worktree)
├── .git/ ← 隐藏文件夹,存所有历史
├── README.md ← 内容:"main 分支的 README"
└── main.c ← 内容:printf("main");
此时 git branch 能看到 main、feature-a、hotfix 3 个分支。但文件夹只有一个 ,显示的是当前检出的 main 分支内容。
第 2 步:执行 git worktree add 命令
bash
cd /home/mi/myapp
git worktree add ../myapp-feature-a feature-a
git worktree add ../myapp-hotfix hotfix
执行完后,磁盘上凭空多出两个文件夹:
plaintext
/home/mi/
├── myapp/ ← 原来就有的(主 worktree)
│ ├── .git/ ← 完整的 .git 目录(真正的历史库)
│ ├── README.md ← "main 分支的 README"
│ └── main.c ← printf("main");
│
├── myapp-feature-a/ ← 新出现!
│ ├── .git ← 注意:这是一个"文件",不是文件夹!
│ ├── README.md ← "feature-a 分支的 README"
│ └── main.c ← printf("feature");
│
└── myapp-hotfix/ ← 新出现!
├── .git ← 同样是文件
├── README.md ← "hotfix 分支的 README"
└── main.c ← printf("hotfix");
三个文件夹完全独立,你可以:
- 用 VSCode 同时开 3 个窗口,每个窗口打开一个文件夹
- 在
myapp-feature-a/main.c里改代码,另外两个文件夹纹丝不动 - 在三个文件夹里分别跑
make、npm start,互不影响
关联在哪?关键在那个"指针文件"
用 cat 看一下附加 worktree 里的 .git:
bash
$ cat /home/mi/myapp-feature-a/.git
gitdir: /home/mi/myapp/.git/worktrees/myapp-feature-a
翻译成人话:
"我这个文件夹不存历史。历史都存在主仓库的 .git/ 里。那边才是真老大。"
所以虽然磁盘上看起来是 3 个独立文件夹,但两个附加 worktree 都通过这个小指针挂在 主仓库的 .git/ 上。
/home/mi/myapp-hotfix/
/home/mi/myapp-feature-a/
/home/mi/myapp/ (主 worktree)
指向
指向
.git/
真·历史库
objects / refs / HEAD ...
README.md / main.c
(main 分支内容)
.git 指针文件
gitdir: .../worktrees/feature-a
README.md / main.c
(feature-a 分支内容)
.git 指针文件
gitdir: .../worktrees/hotfix
README.md / main.c
(hotfix 分支内容)
橘黄色 = 真仓库,存着所有 commit 历史。淡蓝色 = 空壳,只有代码文件和一个指向主仓库的小指针。
生活化类比:图书馆的阅览室
把 Git 仓库想象成一座图书馆:
| 概念 | 图书馆里对应什么 |
|---|---|
.git 目录 |
📚 总书库(所有书的所有版本都存在这里) |
| worktree | 📖 阅览室,从书库里调出某一版本的书到桌上看/改 |
git worktree add |
🏗️ 新开一间阅览室,再调一份不同版本的书出来 |
- 3 间阅览室(
myapp/、myapp-feature-a/、myapp-hotfix/)物理上是 3 个独立房间,互不干扰 - 但它们共用同一个书库 ,你在任一阅览室把涂改稿"归档"(
git commit),书库里立刻多一本新版本,其他阅览室也能看到
自测三道题
读完上面内容,试着回答:
✅ Q1 :在
myapp-feature-a/main.c改了代码但没 commit,myapp/main.c会变吗?→ 不会,三个文件夹的工作区完全独立。
Q2 :在
myapp-feature-a/里git commit后,到myapp/执行git log feature-a,能看到这个 commit 吗?→ 能 ,commit 进了共享的
.git书库,所有 worktree 都能看到。Q3 :把
/home/mi/myapp/(主 worktree)整个删掉,另外两个还能用吗?→ 不能 ,它们的
.git只是指针,指向的真书库没了就全废了。
核心架构:共享 .git,独立工作区
.git 对象库
objects / refs / packed-refs
全局唯一一份
worktree #1(主)
HEAD → main
独立 index / 工作区
worktree #2
HEAD → feature-a
独立 index / 工作区
worktree #3
HEAD → hotfix
独立 index / 工作区
关键点:
.git只有一份 → 省磁盘、省带宽- 每个 worktree 有自己的
HEAD和index→ 互不干扰 - 一处
git fetch,所有 worktree 都能看到新分支
典型场景:紧急修 bug 不打断手头工作
远端仓库 hotfix 工作区(新建) feature 工作区 开发者 远端仓库 hotfix 工作区(新建) feature 工作区 开发者 正在写 feature,代码未完成 无需 stash,无需切分支 上下文完全保留, 改到一半的代码还在 git worktree add ../myapp-hotfix hotfix 定位 bug → 修复 → git commit git push 回到 feature 工作区继续编码
不用 git stash,不用切分支,不用 clone 第二份仓库。
对比:git clone 两份 vs git worktree
方案 B:git worktree
.git
100GB(共享一份)
worktree 1
worktree 2
✅ 磁盘不翻倍
✅ fetch 一次全同步
✅ 分支实时可见
方案 A:git clone 两份
.git #1
100GB
.git #2
100GB
❌ 磁盘翻倍
❌ fetch 两次
❌ 分支状态不同步
仓库越大,优势越明显(想象一下 100GB 的 Chromium 仓库)。
该不该用 worktree?决策图
否,只是偶尔切分支
是
是
否
是
否
是
否
需要在多分支间工作?
会并行推进
2 个以上任务?
git switch / git stash
就够了
仓库很大
或编译很慢?
✅ 强烈推荐
git worktree
需要并行跑
测试 / CI?
习惯多窗口
并行编辑?
常用命令速查
| 操作 | 命令 |
|---|---|
| 基于已有分支开 worktree | git worktree add ../repo-feat feature-a |
| 开 worktree 并新建分支 | git worktree add -b new-branch ../repo-new main |
| 列出所有 worktree | git worktree list |
| 移除 worktree(先删目录) | git worktree remove ../repo-feat |
| 清理无效元数据 | git worktree prune |
| 锁定 worktree(防误删) | git worktree lock ../repo-feat |
典型用途汇总
🚀
- 并行开发 --- 手上的 feature 没写完,随时开 worktree 修 hotfix
- 长编译场景 --- 主目录编译中,另开 worktree 写下一个任务(Vela/NuttX 编译慢场景非常实用)
- Code Review --- 拉同事分支到独立目录看代码,不污染自己工作区
- 版本对比 --- 同时检出 old/new,并排 diff 或对比测试
- CI / 脚本任务 --- 自动化任务在独立 worktree 跑,不干扰开发者
注意事项(踩坑清单)
⚠️
- 🚫 同一分支不能被两个 worktree 同时检出 --- 会报错
already checked out- 🧹 删 worktree 目录后必须
git worktree prune--- 否则.git/worktrees/残留元数据- 🔒 HEAD / index / reflog 每个 worktree 独立 --- 但对象库共享
- 📦
git stash是每个 worktree 独立的 --- 不能跨 worktree 访问 stash
在 Vela/NuttX 项目里
p65-trunk 这类 Google repo 管理的多仓库项目,原生 git worktree 只管单仓库。要一次性给所有子仓开 worktree,用项目里的 repoworktree(rwt) 工具更合适 ------ 它封装了对整个 repo manifest 下所有仓库批量开 worktree 的逻辑,能为每个 feature 开一个完整的多仓隔离工作空间。