很多开发者都遇到过这样的场景:你正在一个功能分支上写代码,修改了十几个文件,程序暂时还跑不起来,也不适合提交。就在这时,同事突然告诉你,线上出现了一个紧急问题,需要马上从 main 分支拉出一个 Hotfix。
按照传统做法,你可能会先执行:
bash
git stash
git switch main
git pull
git switch -c hotfix/xxx
修复完成以后,再回到原来的分支:
bash
git switch feature/xxx
git stash pop
如果只是偶尔操作一次,这种方式倒也能用。但当你同时维护多个 PR、审查别人代码、修复线上问题,或者需要在不同版本之间反复测试时,频繁执行 stash 和 switch 很容易让工作现场变得混乱。
Git 其实提供了一个非常适合这种场景的功能:Git Worktree。

内容结构概览
本文将介绍:
- Git Worktree 是什么;
- 它与
git switch、git stash和重新clone有什么区别; - 如何创建、查看和删除 Worktree;
- 如何在开发新功能的同时处理紧急 Bug;
- 多个 PR、代码审查和版本测试中的使用方法;
- 常见问题与容易踩到的坑。
一、Git Worktree 是什么?
Git Worktree 可以让一个 Git 仓库同时拥有多个工作目录,每个目录分别检出不同的分支。
普通情况下,一个仓库只有一个代码目录:
text
my-project/
这个目录某一时刻只能显示一个分支。你从 main 切到 feature/login 后,目录中的代码也会随之变化。
使用 Worktree 后,可以变成这样:
text
code/
├── my-project/ # main
├── my-project-feature-login/ # feature/login
└── my-project-hotfix/ # hotfix/payment-timeout
三个目录属于同一个 Git 仓库,但分别检出了三个不同分支。
你可以在第一个目录运行稳定版代码,在第二个目录继续开发新功能,同时在第三个目录修复线上问题。它们彼此独立,不需要来回切换分支,也不需要把未完成的代码临时藏进 stash。
Git 官方把最初通过 git clone 或 git init 创建的工作目录称为 main worktree ,后来通过 git worktree add 创建的目录称为 linked worktree 。一个普通仓库可以拥有一个主工作区,以及零个或多个附加工作区。(Git)
二、Worktree 到底共享什么?
Worktree 并不是重新复制出一份完整的 Git 仓库。
多个 Worktree 会共享:
- Git 提交历史;
- 对象数据库;
- 分支和标签引用;
- 远程仓库信息;
- 大部分 Git 配置。
但每个 Worktree 都拥有自己的:
- 工作目录;
- 当前检出的分支;
HEAD;- 暂存区,也就是 index;
- 未提交修改;
- 未跟踪文件。
可以把它理解为:所有工作区共用一个仓库档案室,但每个工作区都有自己的桌面。
text
共享的 Git 仓库
commits / objects / refs
│
┌──────────────┼──────────────┐
│ │ │
Worktree 1 Worktree 2 Worktree 3
main feature hotfix
独立文件 独立文件 独立文件
独立 HEAD 独立 HEAD 独立 HEAD
独立 index 独立 index 独立 index
Git 官方文档也说明,不同 Worktree 的 HEAD 等状态彼此独立,而普通分支引用通常由各个 Worktree 共享。(Git)
三、为什么不直接重新 clone 一份?
当然可以重新 clone:
bash
git clone git@github.com:example/project.git project-main
git clone git@github.com:example/project.git project-feature
git clone git@github.com:example/project.git project-hotfix
但这样会得到三套独立仓库:
text
project-main/.git/
project-feature/.git/
project-hotfix/.git/
对于大型仓库,这意味着:
- Git 对象可能被重复存储;
- 每个仓库都要单独执行
fetch; - Remote、Hook 和部分配置可能不一致;
- 本地仓库数量越来越多,很难管理;
- 创建新环境的速度比 Worktree 慢。
Worktree 复用的是同一套 Git 仓库数据,因此创建速度通常很快,也更加节省空间。
不过要注意,Worktree 只共享 Git 数据,并不会共享所有构建产物。例如下面这些目录通常仍然是各自独立的:
text
node_modules/
target/
dist/
vendor/
.venv/
因此两个大型 Rust Worktree 仍可能分别生成自己的 target 目录,占用较多磁盘空间。
四、查看当前已有的 Worktree
进入任意一个工作区,执行:
bash
git worktree list
可能得到:
text
/Users/mac/code/my-project a1b2c3d [main]
/Users/mac/code/my-project-login d4e5f6g [feature/login]
/Users/mac/code/my-project-hotfix h7i8j9k [hotfix/payment-timeout]
每一行分别表示:
text
工作目录路径 当前提交 当前分支
查看更详细的信息:
bash
git worktree list --verbose
需要在 Shell 脚本中处理输出时,可以使用:
bash
git worktree list --porcelain
五、为已有分支创建 Worktree
假设仓库中已经存在:
text
feature/login
当前主仓库位于:
text
~/code/my-project
可以执行:
bash
cd ~/code/my-project
git worktree add \
../my-project-feature-login \
feature/login
Git 会创建新目录:
text
~/code/my-project-feature-login
并在里面检出 feature/login。
现在可以直接进入新目录:
bash
cd ../my-project-feature-login
git status
输出会显示:
text
On branch feature/login
此时主目录仍然保持原来的分支和所有未提交修改,完全不会受到影响。
六、创建新分支时同时创建 Worktree
这是最常用的方式。
假设要从最新的 origin/main 创建一个 Hotfix:
bash
git fetch origin
git worktree add \
-b hotfix/payment-timeout \
../my-project-payment-timeout \
origin/main
这条命令同时完成三件事:
- 基于
origin/main创建hotfix/payment-timeout; - 创建目录
../my-project-payment-timeout; - 在新目录中检出该分支。
它的通用格式是:
bash
git worktree add -b <新分支名> <新目录> <起始提交>
起始提交可以是:
text
main
origin/main
release/v2
v2.1.0
某个 commit hash
例如:
bash
git worktree add \
-b fix/readme-typo \
../my-project-fix-readme \
origin/main
我通常建议明确写出 origin/main,而不是省略起点。这样可以避免本地 main 很久没有更新,导致新分支建立在旧提交上。
七、实战:开发未完成时紧急修复 Bug
假设你正在 ethereumjs-monorepo 中开发一个功能,当前分支有不少未提交修改:
bash
git status
输出类似:
text
On branch feature/trie-refactor
Changes not staged for commit:
modified: packages/trie/src/trie.ts
modified: packages/trie/test/trie.spec.ts
现在你需要基于上游最新代码修复一个 README typo。
不需要 stash,直接执行:
bash
cd ~/code/ethereumjs-monorepo
git fetch upstream
git worktree add \
-b fix-util-readme-typo \
../ethereumjs-fix-util-readme-typo \
upstream/master
如果项目默认分支是 main,则改成:
bash
git worktree add \
-b fix-util-readme-typo \
../ethereumjs-fix-util-readme-typo \
upstream/main
进入新的工作区:
bash
cd ../ethereumjs-fix-util-readme-typo
完成修改:
bash
git add packages/util/README.md
git commit -m "docs(util): fix README typo"
git push -u origin fix-util-readme-typo
此时目录结构可能是:
text
~/code/
├── ethereumjs-monorepo/
│ └── feature/trie-refactor,保留未提交修改
│
└── ethereumjs-fix-util-readme-typo/
└── fix-util-readme-typo,准备提交 PR
两个工作区互不干扰。
修复完成后,你可以在浏览器里提交 PR,也可以使用 GitHub CLI:
bash
gh pr create
然后回到原来的目录继续开发:
bash
cd ../ethereumjs-monorepo
不需要执行:
bash
git stash
git switch
git stash pop
也不需要重新启动或恢复原来分支的工作状态。
八、PR 合并后如何清理?
先确认工作区中没有需要保留的修改:
bash
cd ../ethereumjs-fix-util-readme-typo
git status
然后回到主仓库或者任意其他 Worktree:
bash
cd ../ethereumjs-monorepo
删除附加工作区:
bash
git worktree remove ../ethereumjs-fix-util-readme-typo
注意,git worktree remove 会删除对应的工作目录,但不会自动删除分支。
如果本地分支已经合并,可以继续执行:
bash
git branch -d fix-util-readme-typo
清理远程已经删除的跟踪引用:
bash
git fetch --prune origin
完整清理流程如下:
bash
git worktree remove ../ethereumjs-fix-util-readme-typo
git branch -d fix-util-readme-typo
git fetch --prune origin
Git 官方建议使用 git worktree remove 删除附加 Worktree,而不是直接删除目录。(Git)
九、为什么不能直接执行 rm -rf?
下面这种操作并不推荐:
bash
rm -rf ../my-project-hotfix
因为目录虽然被删除了,但主仓库内部可能仍然保留该 Worktree 的管理信息。
这时执行:
bash
git worktree list
可能仍会看到已经不存在的工作区。
可以使用:
bash
git worktree prune
清理失效记录。
正式删除时,最好始终使用:
bash
git worktree remove <工作区路径>
如果目录已经被手动删除,再执行:
bash
git worktree prune
查看哪些记录将被清理,但暂时不执行:
bash
git worktree prune --dry-run --verbose
十、工作区中有未提交代码时怎么办?
假设某个 Worktree 中仍然存在修改:
bash
git status
显示:
text
Changes not staged for commit
此时执行:
bash
git worktree remove ../my-project-hotfix
Git 通常会拒绝删除,因为这会导致未提交内容丢失。
正确做法是先决定如何处理这些代码:
方案一:提交代码
bash
git add .
git commit -m "wip: preserve current work"
方案二:暂存修改
bash
git stash push -u
方案三:确认不再需要,强制删除
bash
git worktree remove --force ../my-project-hotfix
--force 可能造成代码永久丢失,使用前应确认该目录中没有任何需要保留的文件。
十一、一个分支不能同时在两个 Worktree 中检出
假设 main 已经在主工作区中使用:
bash
git worktree list
输出:
text
/Users/mac/code/my-project a1b2c3d [main]
此时再执行:
bash
git worktree add ../my-project-main-copy main
Git 通常会拒绝,并提示:
text
fatal: 'main' is already checked out
这是为了防止两个工作目录同时修改同一个分支指针。
假设你只是想基于 main 做实验,应该创建一个新分支:
bash
git worktree add \
-b experiment/new-parser \
../my-project-new-parser \
main
或者创建一个 detached HEAD 工作区:
bash
git worktree add \
--detach \
../my-project-main-test \
main
默认情况下,Git 会阻止同一个分支同时出现在多个 Worktree 中。(Git)
十二、用 Worktree 审查别人的 PR
Worktree 不仅适合开发,也非常适合代码审查。
假设要审查 GitHub PR #1234,可以先拉取 PR 分支:
bash
git fetch origin pull/1234/head:review/pr-1234
然后创建 Worktree:
bash
git worktree add \
../my-project-review-1234 \
review/pr-1234
进入该目录:
bash
cd ../my-project-review-1234
在不影响当前开发环境的情况下运行:
bash
go test ./...
或者:
bash
cargo test
审查结束后:
bash
cd ../my-project
git worktree remove ../my-project-review-1234
git branch -D review/pr-1234
这样每个 PR 都可以拥有独立的测试环境,不需要频繁切换当前仓库的分支。
十三、同时维护多个 PR
对于经常给开源项目提交贡献的人,Worktree 非常实用。
假设正在处理三个 PR:
text
fix/readme-typo
fix/parser-comment
test/new-analyzer
可以分别创建:
bash
git fetch upstream
git worktree add \
-b fix/readme-typo \
../project-fix-readme \
upstream/main
git worktree add \
-b fix/parser-comment \
../project-fix-parser-comment \
upstream/main
git worktree add \
-b test/new-analyzer \
../project-new-analyzer \
upstream/main
目录结构变成:
text
code/
├── project/
├── project-fix-readme/
├── project-fix-parser-comment/
└── project-new-analyzer/
每个目录可以分别打开一个终端或 IDE 窗口。
等待第一个 PR 的 CI 时,可以直接进入第二个目录继续工作:
bash
cd ../project-fix-parser-comment
这种方式比不断 switch 分支更加自然,也不容易把不同 PR 的文件混在一起。
不过,同时提交多个 PR 时仍应注意项目维护者的体验。相关的小修改最好合并处理,不要为了增加提交数量而拆成大量价值很低的 PR。
十四、测试旧版本或历史提交
Worktree 也很适合临时查看某个版本。
例如,需要测试 v1.5.0:
bash
git worktree add \
--detach \
../my-project-v1.5.0 \
v1.5.0
进入该目录:
bash
cd ../my-project-v1.5.0
完成编译或测试后删除:
bash
cd ../my-project
git worktree remove ../my-project-v1.5.0
因为使用了 --detach,这个工作区没有绑定普通分支,适合:
- 复现旧版本 Bug;
- 比较两个版本的性能;
- 编译某个 Tag;
- 检查历史提交;
- 执行临时实验。
十五、移动 Worktree
不要优先使用普通的 mv 命令移动工作区。
推荐执行:
bash
git worktree move \
../my-project-hotfix \
../worktrees/my-project-hotfix
如果已经使用 Finder 或 mv 手动移动了目录,导致 Git 的路径记录失效,可以尝试:
bash
git worktree repair
也可以指定路径:
bash
git worktree repair ../worktrees/my-project-hotfix
当前 Git Worktree 命令提供了 move 和 repair,分别用于移动工作区和修复工作区关联。(Git)
十六、外置硬盘上的 Worktree
有时 Worktree 放在移动硬盘、网络磁盘或者临时挂载目录中。磁盘没有挂载时,Git 可能认为该工作区已经失效,并在后续清理中删除管理记录。
可以锁定该 Worktree:
bash
git worktree lock \
--reason "Stored on external SSD" \
/Volumes/SSD/my-project-release
解除锁定:
bash
git worktree unlock \
/Volumes/SSD/my-project-release
日常放在本机固定目录中的 Worktree 通常不需要执行 lock。这个功能主要用于路径可能暂时不可访问的工作区。Git 官方命令集中也包含 lock 和 unlock。(Git)
十七、推荐的目录组织方式
不建议把 Worktree 建在主仓库内部:
text
my-project/
└── worktrees/
└── feature-login/
这样可能导致:
- IDE 重复索引大量文件;
- 搜索结果出现多个版本;
- 文件监听器监控额外目录;
- 构建工具误扫描另一个工作区;
- 主仓库出现复杂的未跟踪目录。
更推荐使用并列目录:
text
~/code/
├── ethereumjs-monorepo/
├── ethereumjs-fix-readme/
└── ethereumjs-trie-refactor/
或者统一放在专门目录:
text
~/code/
├── ethereumjs-monorepo/
└── worktrees/
└── ethereumjs/
├── fix-readme/
├── trie-refactor/
└── review-pr-1234/
对应命令:
bash
git worktree add \
-b fix/readme \
~/code/worktrees/ethereumjs/fix-readme \
upstream/main
目录名称不必与分支名完全相同,但保持一致会更容易管理。
十八、Worktree、stash 和重新 clone 怎么选?
三种方式分别适合不同场景。
| 场景 | 推荐方式 |
|---|---|
| 临时离开当前分支几分钟 | git stash |
| 两个任务需要并行开发 | Worktree |
| 同时运行新旧两个版本 | Worktree |
| 审查多个 PR | Worktree |
| 复现某个历史版本问题 | Worktree |
| 需要完全不同的 Remote 和 Git 配置 | 重新 clone |
要破坏性修改 .git 数据 |
重新 clone |
| 多个 AI Agent 同时处理不同任务 | Worktree |
简单来说:
stash是临时把桌面收起来,Worktree 是再准备一张桌子,重新 clone 则是再租一个办公室。
十九、常用命令速查
查看全部 Worktree:
bash
git worktree list
为已有分支创建 Worktree:
bash
git worktree add <目录> <已有分支>
创建新分支并创建 Worktree:
bash
git worktree add -b <新分支> <目录> <起点>
创建 detached HEAD 工作区:
bash
git worktree add --detach <目录> <提交或标签>
删除 Worktree:
bash
git worktree remove <目录>
强制删除:
bash
git worktree remove --force <目录>
移动 Worktree:
bash
git worktree move <旧路径> <新路径>
清理失效记录:
bash
git worktree prune
修复关联:
bash
git worktree repair
锁定外置工作区:
bash
git worktree lock <目录>
解除锁定:
bash
git worktree unlock <目录>
二十、总结
Git Worktree 解决的问题并不复杂:
它允许同一个 Git 仓库同时在多个目录中检出不同分支。
过去,我们常常需要在一个目录中不断执行:
bash
git stash
git switch
git stash pop
使用 Worktree 后,很多时候只需要切换目录:
bash
cd ../project-feature
cd ../project-hotfix
每个任务都有自己的目录、分支、暂存区和未提交修改。正在运行的开发服务器、IDE 窗口以及测试环境也可以继续保持,不需要因为切换分支而反复重启。
它尤其适合:
- 同时维护多个 PR;
- 开发新功能时紧急修复 Bug;
- 审查其他人的代码;
- 对比多个版本;
- 同时运行多个测试环境;
- 让多个自动化工具或 AI Agent 并行工作。
掌握 Worktree 后,Git 分支不再只是需要不断切换的状态,而可以变成多个真正并行存在的工作空间。
对于经常参与大型开源项目、同时维护多个分支的开发者来说,这可能是 Git 中最值得养成使用习惯的功能之一。
参考资料
Git 官方 git-worktree 文档,对 Worktree 的创建、查看、删除、移动、清理、锁定和修复等行为给出了完整定义。(Git)