1. 为什么需要worktree:并行开发场景下的现实痛点
1.1 一个真实的多任务现场
先还原一个我前几天遇到的场景。朋友在改一个订单模块,代码写到一半,线上突然报了个紧急问题,需要马上拉分支修复。他当时的处理方式是:把现有改动 git stash,切回主分支,拉一个 hotfix 分支,修完发布,再切回原来的分支,git stash pop。这一套流程看着没毛病,但实际操作中你总会遇到几个让人头疼的瞬间,比如 stash pop 时冲突了、切分支时发现刚才某个文件忘了保存、build 到一半被迫中断,又或者仅仅是“我改了哪几个文件”这件事本身,就已经记不清了。
这就是 git worktree 存在的意义。它一句话解释:让同一个 Git 仓库,拥有多个相互独立、互不干扰的工作目录。每个目录可以检出不同的分支,你可以一边在主目录继续开发新功能,一边在另一个目录紧急修 bug,两边各自 commit、各自 build、各自测试,完全不打架。
当时我就直接让朋友别折腾了,用 worktree 把 hotfix 单独开一个目录出来,两边同时开工,几分钟就搞定了。这个功能我用了几年,今天系统性地把它的原理、操作和坑讲清楚,适合已经能熟练使用 git branch、git checkout、git merge 的开发者进阶学习。
1.2 传统方案的局限:stash、clone 与 checkout
在没有 worktree 之前,想同时处理多个分支上的任务,我们通常只有三种思路,但每一种都有明显的代价。
第一种是 git stash 加 git checkout 反复切换。这种方案最伤的不是操作繁琐,而是你一直在“中断-恢复”循环里。开发的思路被打断,build 产物要重新生成,stash 里的内容放久了甚至可能自己都忘了里面存的什么。一旦 stash 期间代码库发生大量变更,pop 时冲突几乎是必然的。
第二种是直接 git clone 多份仓库。这种做法倒也简单粗暴,每个目录就是一个独立的完整仓库。但代价非常明显:存储空间翻倍,而且多份 clone 之间的分支、提交、标签完全各过各的,你在这份 clone 上新建的分支并不会自动出现在另一份 clone 里,靠 push/pull 来同步简直是给自己找罪受。
第三种是只用一个工作目录,靠 git branch 切换。这本身没有错,但有一个前提是:你真的每次只需要处理一个分支。如果你需要同时打开两个分支的代码对比,或者并行推进两个任务,单工作目录天然不支持这种用法。
这三种方案的共同痛点是什么?说到底,工作区只有一个,而任务有多个。worktree 解决的正是这个矛盾:仓库只有一个,但工作区可以开出多个。
1.3 worktree 解决的核心问题
git worktree 功能是从 Git 2.5 开始引入的,到 2.15 之后各种边界情况基本完善。它允许你在同一个仓库里同时检出多个分支到不同目录,这些目录共享同一个 .git 对象库、共享所有分支和提交记录,但每个目录拥有独立的文件状态、独立的暂存区、独立的 HEAD。
这意味着什么呢?打个比方,一个 Git 仓库就像一套房子的总配电箱,对象库就是总闸,所有电路都从这里接。worktree 相当于从同一块电表上拉出几路独立的插座,每个插座上插的电器(工作目录)之间互不干扰——你用微波炉不会影响另一边开着空调,只要你别在同一路插座上插两个大功率设备就行。
对照到实际场景,这套机制直接解决了四个高频需求:
- 紧急修复与日常开发并行。功能分支继续写着,hotfix 分支在另一个目录里修,互不打断;
- Code Review 更舒服。有人提了个 PR/MR,你直接
git worktree add一个目录来检出那个分支,边看代码边运行测试,看完就删,不污染主工作区; - 同一仓库多版本同时构建。比如一个目录是 main 分支,一个目录是某个 release 分支,两边各自跑构建、跑测试,不用排队等切换;
- 实验性改动零风险尝试。想在某个提交上试试新写法,又不想影响当前分支,直接开一个 detached HEAD 的 worktree 进去折腾就行。
这几个需求技术上并不算新问题,但 worktree 把这套操作从“勉强能做”变成了“优雅自然”,这就是它最大的价值。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. worktree 核心原理与工作方式
2.1 一个仓库,多个工作目录的底层机制
很多人第一次接触 worktree 时会觉得不可思议:一个仓库怎么可能同时检出两个分支?Git 的底层机制里到底发生了什么?
先说说普通仓库的结构。一个正常的 Git 仓库里,.git 是核心目录,里面保存着对象数据库(objects)、引用(refs)、HEAD、暂存区(index)等。你每次 checkout 一个分支,本质上就是:把 HEAD 指向某个分支引用,然后把该分支对应的快照内容写到工作目录里。
而当你执行 git worktree add 时,Git 做了这么几件事:
- 在主仓库的
.git/worktrees/<name>/下新建一个目录,里面放这个新 worktree 自己专属的 HEAD、index、per-worktree refs; - 在你指定的路径创建真实的文件目录,并把对应分支的文件内容完整检出到里面;
- 在这个新目录下创建一个
.git文件(注意,不是目录),内容是一行文本,指向主仓库的 gitdir 路径,格式类似:gitdir: /path/to/main-repo/.git/worktrees/<name>。
这个机制的巧妙之处在于:每个 worktree 的“身份信息”独立存储,但“内容数据库”完全共享。主仓库与所有 linked worktree 共享 objects(提交、树、二进制对象)、共享 refs(branches、tags),所以你在任何一个 worktree 里 git commit 之后,其他 worktree 能立刻看到新的提交记录;你在任何一个 worktree 里新建分支,其他 worktree 用 git branch 也能立刻看到。
但另一方面,每个 worktree 的工作区文件、暂存区、HEAD 又是完全独立的。在 A 目录里改了一半的文件,B 目录里完全看不到,B 的 git status 依旧干净,反之亦然。这就是“共享历史、隔离工作区”的精确含义。
2.2 worktree 与 branch 的本质区别
网上搜 worktree 相关的热词,出现频率最高的就是“git worktree 与 git branch 区别”。这俩确实容易混,因为很多人习惯性把 worktree 理解成“一种特殊的分支”,实际上它们的维度完全不一样。
我用一句话概括:branch 是提交历史上的一条线,worktree 是物理磁盘上的一个目录。branch 关心的是“我的提交从哪来、往哪走”,worktree 关心的是“我这份代码文件实际存在哪里”。
具体差异用表格看更清楚:
| 对比维度 | git branch | git worktree |
|---|---|---|
| 本质 | 指向某次提交的可移动引用/指针 | 一个实际存在于磁盘上的独立工作目录 |
| 作用层级 | 提交历史、引用层面 | 文件系统、工作区层面 |
| 创建之后的影响 | 只是新增一个引用,当前工作区不变 | 立刻创建一个新目录,并完整检出文件 |
| 切换方式 | git checkout / git switch 在当前目录切换 |
直接 cd 到不同目录,天然处于不同分支 |
| 是否可以同时存在多个 | 可以,仓库里同时有几十个分支很正常 | 可以,但每个分支同一时刻只能被一个 worktree 检出 |
| 磁盘占用 | 几乎为零(只是一条引用) | 每新增一个 worktree,就等于重新完整 checkout 一份工作区文件 |
| 典型用途 | 管理开发线、记录版本演进 | 并行做多个任务、独立验证不同分支 |
这个表里最值得细说的是“每个分支同一时刻只能被一个 worktree 检出”。这是 Git 的一条硬性规则。试想一下,如果两个 worktree 都检出了同一个分支,两边同时改同一个文件再同时 commit,Git 根本没法判断哪个才是真正的“当前状态”。所以当你尝试在一号 worktree 里 git worktree add ../other <当前已检出的分支> 时,会收到一个明确的报错:fatal: '<branch>' is already checked out at '...'。
理解了这层区别,你就不会再犯“把 worktree 当分支来用”的错误。worktree 和 branch 不是二选一的关系,而是互补关系:先用 branch 决定你要做什么事,再用 worktree 决定你在哪个目录里做这件事。实际工作流里,worktree 通常和分支密切相关,但它本身解决的问题是“工作区隔离”,而不是“提交管理”。
2.3 worktree 的目录结构与共享机制
如果你想看一个仓库当前挂载了哪些 worktree,在任意一个 worktree 里执行:
bash复制git worktree list
输出大概长这样:
code复制/Users/me/projects/myapp main abc1234 [main]
/Users/me/projects/myapp-hotfix hotfix/urgent def5678 [hotfix/urgent]
/Users/me/projects/myapp-experiment fix/nav 1234abc [fix/nav]
第一列是 worktree 所在的物理路径,第二列是当前检出的分支,第三列是当前 HEAD 指向的提交。这是你日常管理多个目录时最高频使用的命令。
了解原理的话,可以顺着目录进去看一眼。主仓库的 .git/worktrees/ 下面会多出几个子目录,每个子目录对应一个 linked worktree:
text复制.git/
objects/ # 对象库(全局共享)
refs/ # 引用(全局共享)
HEAD # 主工作区的 HEAD
index # 主工作区的暂存区
worktrees/
hotfix/
HEAD # 该 worktree 自己的 HEAD
index # 该 worktree 自己的暂存区
commondir # 指向主仓库 .git 的文件(内容就是主仓库 .git 的实际路径)
experiment/
HEAD
index
commondir
而每个 linked worktree 的工作目录里只有一行内容的 .git 文件。这个细节很多人不知道,但它解释了为什么一个目录可以凭空变成一个 Git 仓库的“分身”:Git 启动时遇到一个 .git 文件,会按文件里的 gitdir: 路径去加载对应的 gitdir,然后此目录就拥有了和主仓库一样的对象库和引用视图。
理解这个机制后,下面几个现象你就能很自然地明白了:
- 有一个 worktree 的目录被手动删除后,主仓库的
git worktree list里会残留失效记录,需要用git worktree prune清理; - 主仓库中的
.git/worktrees/<name>目录被删除后,对应 worktree 目录里的.git文件就会变成悬空指针,这个 worktree 基本就废了; - Git 2.17 之后支持
git worktree move命令,可以把 worktree 移到新路径,Git 会自动更新引用和.git文件里的路径信息。
这套设计在文件系统层面是干净利落的,理解了它,排查问题时会少走很多弯路。
3. worktree 完整实操指南
3.1 前置准备与基本配置
实操之前先确认你的 Git 版本。worktree 是 Git 2.5 引入的,建议用 2.15 以上的版本,因为 2.15 之后修复了不少和 worktree 相关的边界 bug,比如 git worktree remove 的稳定性。直接用下面命令查看:
bash复制git --version
如果你还停留在 2.20 以下的版本,我建议顺手升个级。这不是说旧版本用不了,而是有些新特性(比如 git worktree remove 和 git worktree move 的完善)在旧版本上体验差距明显。如果你用的 IDE 内置 Git,也要注意 IDE 自带 Git 的版本,个别老版本 VS Code 绑定的 Git 可能比较旧,遇到问题先在终端里确认 git --version。
另外强调一个最基础的坑:worktree 命令必须在某个 Git 仓库目录内执行。很多人第一次用的时候随便找个文件夹就敲 git worktree add,结果收到一个让人摸不着头脑的报错:
text复制error: cannot create agent worktree: not in a git repository and no worktree
这个报错我们在第 4 节会专门讲,现在只要记住一点:先 cd 到你要管理的仓库根目录,确认 git status 能正常输出,再执行 git worktree 相关命令。
3.2 创建 worktree 的两种方式与参数选择
worktree 的创建命令核心就是 git worktree add,但根据场景不同有几种常用变体。我用一个实际需求串起来讲:假设你当前在 /Users/me/projects/myapp 这个仓库的 main 分支上,现在线上出了紧急 bug,你打算在 /Users/me/projects/myapp-hotfix 这个目录里拉一个 hotfix/payment 分支来修复。
方式一:基于当前 HEAD 直接创建新分支并关联
这是最常用的方式:
bash复制git worktree add -b hotfix/payment ../myapp-hotfix
命令里 -b 表示基于当前 HEAD 创建一个新分支,../myapp-hotfix 是新 worktree 的物理路径。执行完,Git 自动完成三件事:创建 hotfix/payment 分支、在新目录检出文件、把新分支和新目录绑定。你直接 cd ../myapp-hotfix 就能开始干活。
方式二:基于指定分支或提交创建 worktree
如果你想在某个已有分支上开 worktree,比如要评审 release/2.0 分支的代码,那就不要加 -b:
bash复制git worktree add ../myapp-release release/2.0
这条命令会直接基于 release/2.0 分支检出文件,进入新目录时 HEAD 就指向这个分支。这种方式创建的 worktree 通常用于只读检查、构建测试,如果你想在这个 worktree 里提交代码,记得先确认它检出的分支没有在其他 worktree 被占用。
如果只是想在某个历史提交上临时验证代码,可以用 --detach 参数:
bash复制git worktree add --detach ../myapp-verify 8f3a1b2
这条命令基于指定 commit 8f3a1b2 创建了一个游离的 HEAD 状态,适合做代码考古或临时实验。
三种方式的选择逻辑总结成一句话:新需求用 -b 新建分支,看别人的分支不带 -b,看历史提交加 --detach。
3.3 日常使用与清理维护
创建完 worktree 之后,日常使用其实不需要任何特殊命令——cd 进去,git status、git diff、git commit、git push 全都照常使用。
但有几个管理动作你需要掌握。
查看所有 worktree 的状态
bash复制git worktree list
这个命令前面说过,是日常使用频率最高的。如果脚本需要解析输出,可以加 --porcelain 参数,输出格式更稳定,适合程序读取:
bash复制git worktree list --porcelain
清理不再需要的 worktree
当 hotfix 修复完成、分支已经合并并 push 到远程之后,就可以把这个 worktree 从磁盘上移除了:
bash复制git worktree remove ../myapp-hotfix
这条命令会删除 myapp-hotfix 目录,同时把 .git/worktrees/hotfix 对应的元数据清掉。如果这个 worktree 里有未提交的修改或者未跟踪的文件,Git 会拒绝删除并给出提示,你需要先处理掉这些改动,或者用 --force 强制删除:
bash复制git worktree remove --force ../myapp-hotfix
--force 会直接丢弃所有未提交的改动,不可恢复,使用前务必确认里面没有你需要的东西。
清理残留的失效记录
有时候你手滑直接把 worktree 的文件夹删了(比如在文件管理器里删的),这时候 git worktree list 会显示一条仍然存在的记录,但对应的目录已经没了。这种情况用 prune 清理:
bash复制git worktree prune
它会扫描所有 worktree 记录,把哪些对应的 .git/worktrees/<name> 元数据已经不存在的记录清理掉。这就是个“扫地”操作,可以放心大胆地跑。
锁定与解锁
如果某个 worktree 的目录比较重要,或者你暂时不希望它被 prune 误清理,可以给它加个锁:
bash复制git worktree lock ../myapp-customer
git worktree unlock ../myapp-customer
锁定状态在 git worktree list 里会显示 locked 标记。这个功能平时用得少,但在 CI 自动化脚本里很有用——脚本在清理 worktree 时可以跳过锁定的目录。
3.4 一个从创建到清理的完整工作流
把以上命令连起来,一个典型的 hotfix 全流程是这样的:
bash复制cd /Users/me/projects/myapp
# 1. 基于 main 创建 hotfix worktree
git worktree add -b hotfix/payment ../myapp-hotfix
# 2. 进入 hotfix 目录,修复 bug
cd ../myapp-hotfix
git status
# ... 修改文件,测试,提交 ...
git commit -am "fix: payment timeout issue"
git push origin hotfix/payment
# 3. 回到主仓库,合并 hotfix
cd /Users/me/projects/myapp
git checkout main
git merge hotfix/payment
# 4. 推送并清理
git push origin main
git worktree remove ../myapp-hotfix
git branch -d hotfix/payment
这里我特别提醒一个操作习惯:永远先 git worktree remove,再 git branch -d 删除分支。顺序反了虽然大概率也能成功,但一旦 worktree 还停在那个分支上,删除分支会触发 Git 的检查并给出 error: Cannot delete branch ... checked out at ... 的提示,你会平白多一步排查。
4. 常见问题与排查技巧实录
4.1 高频报错速查表
我整理了一份 worktree 高频报错的速查表,都是实际使用中经常出现的,建议收藏备用:
| 报错信息(示意) | 出现原因 | 解决办法 |
|---|---|---|
error: cannot create agent worktree: not in a git repository and no worktree |
在普通文件夹里执行了 git worktree add |
cd 到 Git 仓库内,或者先 git init / git clone 初始化仓库 |
fatal: '<branch>' is already checked out at '/path/to/other' |
该分支已被另一个 worktree 检出,Git 不允许同一分支同时被多个工作区检出 | 去 git worktree list 查看该分支已被哪个目录占用,确认后在对应目录操作,或先 remove 旧 worktree |
fatal: '<path>' already exists |
目标路径已经存在且不是空目录 | 换一个路径,或者先把已存在目录的内容清空/移走 |
fatal: unable to access '.git/worktrees/xxx': Permission denied |
文件系统权限问题,或者目录被其他进程占用 | 检查目录权限,Windows 上检查是否有程序占用了该目录;不能解决时关闭 IDE/文件管理器再试 |
fatal: 'origin/<branch>' is not a commit and a branch '<branch>' cannot be created from it |
-b 创建分支时的基点不对,常见于想从远程分支创建但没写全 |
先 git fetch origin 同步远程引用,再带完整起点,如 git worktree add -b hotfix/xxx ../path origin/main |
warning: detected dubious ownership in repository at ... |
Git 的所有权安全检查,常见于多用户环境或跨设备挂载目录 | 如果是你自己的目录,把当前用户加到 Git 的 safe.directory 配置中(具体搜对应报错,各系统命令一致) |
第一个报错就是热词里出现过的“not in a git repository and no worktree”,它真的非常容易踩。我见过很多人在下载了源码包解压后,直接在那个目录里敲 worktree 命令,然后一脸疑惑。记住一句话:worktree 不是独立的仓库,它是一个仓库的分身,所以必须先有一个本体。
4.2 实操中的几个隐蔽坑点
除了报错之外,还有一些不报错但容易让你迷惑的坑,这些没有踩过的人往往想不到。
坑一:worktree 里不能删除被其他 worktree 使用的分支
假设 A 目录检出了 feature/login,你在 B 目录执行 git branch -d feature/login,会得到 error: Cannot delete branch 'feature/login' used by worktree at '/path/A'。这不是 bug,是保护机制。正确做法是:在 A 目录切走、删除 A 的 worktree,再回来删分支。
坑二:git stash 在不同 worktree 之间不是完全独立的
很多人以为每个 worktree 完全隔离,实际有个例外——stash。因为 stash 的底层是 refs/stash,而 refs 是全局共享的,所以你在 A worktree 里 stash 的内容,在 B worktree 里用 git stash list 也能看到。当然,你可以用 git stash push -m "desc" 区分,但本质上它是共享的。如果你在 A 里 stash,又在 B 里想 git stash pop,可能会把改动恢复到 B 的工作区里——这大概率不是你想要的。所以我在多 worktree 并行时几乎不用 stash,而是习惯性地用临时分支来保存半成品:改到一半就 git commit 到一个临时分支,或者干脆放进自己独立开发分支上。
坑三:submodule 在 worktree 里会比较折腾
如果仓库里有 submodule,情况会复杂一些。worktree 共享的是主仓库的对象库和引用,但 submodule 的检出状态不在共享范围内。实测下来,在新创建的 worktree 里,submodule 目录往往处于未初始化状态,你需要手动执行 git submodule update --init 来拉取。如果 submodule 改动了,还需要注意多个 worktree 之间 submodule 的状态是各自独立的。这个问题官方文档也没有展开细说,建议需要处理 submodule 的团队,尽量在主 worktree 里统一维护 submodule 的更新和提交,减少跨 worktree 操作。
坑四:git worktree remove 与旧版本 Git 的兼容性问题
Git 2.15 之前,git worktree remove 是不能直接删除包含未提交改动的 worktree 的,但在 2.15 之后行为也有变化。如果你在用很老的 Git 版本且 remove 时遇到诡异问题,优先考虑升级 Git 版本而非排查自己的操作。另外,如果你在 CI 脚本里调用 git worktree remove --force,要意识到这个命令会彻底删除工作区文件,脚本里请加上详细的日志输出和路径校验,避免误删。
5. 与 IDE/团队协作的结合与取舍
5.1 在 VS Code 中使用 worktree
现在的 IDE 大多能识别 worktree,但体验上需要一些配置。以 VS Code 为例,直接 code ../myapp-hotfix 打开对应目录就行,VS Code 会根据目录里的 .git 文件自动识别 Git 仓库,分支信息、源码管理面板都能正常工作。
如果你想在 VS Code 里直接可视化创建 worktree,有几种方式。GitLens 扩展里提供了 worktree 管理的入口,可以浏览所有 worktree 并一键新增、切换、删除。也可以用专门的 Git Worktree 扩展,这类插件通常会在侧边栏列出所有 worktree,点击按钮即可创建。创建时一般会让你填四个信息:worktree 名称、基于的分支、存放路径、是否新建分支。填完确认,插件会自动执行 git worktree add 并打开新窗口。
一个使用体验上的小建议:同一个仓库的多个 worktree,尽量用不同的 VS Code 窗口打开,不要试图在同一个窗口的不同终端里来回折腾。因为 VS Code 的源码管理面板是绑定工作区根目录的,你打开的是主目录,它显示的始终是主目录的状态;再开一个终端 cd 到 hotfix 目录里操作,面板不会自动跟着变,容易产生误解。多开窗口是更直观、更不容易错的方式。
5.2 什么情况下不建议用 worktree
worktree 虽然是利器,但不是万能的。以我的经验,下面几种场景下用 worktree 反而添乱。
第一种是仓库包含大量大体积二进制文件。每次 git worktree add 都会完整 checkout 一份工作区文件,对于动辄几百 MB 甚至 GB 级的二进制资产(比如游戏项目、设计资源库、大型离线数据),每开一个 worktree 就相当于复制一份完整工作目录,磁盘开销非常可观。这种情况我建议还是老老实实一个时间只处理一个分支,或者直接用 git sparse-checkout 配合 worktree 来做部分检出。
第二种是你其实并不需要同时做多件事。如果只是偶尔想看看另一个分支的状态,git show branch:file 或者 git log 就能解决,完全没必要开 worktree。开 worktree 是有成本的:多一个目录要跟踪、多一份文件要维护、多一份磁盘开销。用该功能前先问自己一句:我真的需要同时在这个仓库的两个分支上干活吗?
第三种是团队对目录路径有强约定的项目。有些项目缺点或者构建脚本会硬编码相对路径、绝对路径,比如假设代码一定在 D:\code\project 下。这种项目里如果你把 worktree 开到一个新路径,构建可能直接失败。这类问题不是 worktree 本身的锅,但确实会影响使用体验,团队引入 worktree 前最好先排查构建脚本对路径的敏感度。
第四种是Git 版本过低的项目环境。前面反复强调 worktree 在 2.15 之后才稳定,如果你的团队环境还在用 CentOS 7 自带的 Git 1.8、Windows 上万年不更新的老客户端,那就先别折腾 worktree 了,先统一 Git 版本再说。
5.3 我的个人工作流与习惯
最后分享几个我实际用 worktree 时养成的习惯,不一定适合所有人,但至少能帮你少踩一些坑。
第一个习惯是给 worktree 目录命名时带上语义前缀。比如主仓库叫 myapp,功能开发就建 ../myapp-feature-login,hotfix 就建 ../myapp-hotfix-payment,不要随手建 ../test1、../temp 这种名字。半年后你 git worktree list 一看,带语义的名字能让你三秒钟回忆起当时在干什么,而不带语义的名字只能让你满脸问号。为了进一步避免不同 worktree 之间“串台”,我还习惯在 shell 提示符里显示当前目录名和分支名,比如 zsh 配合 pure 或 starship 主题,看到提示符就知道自己在哪个目录、哪个分支,基本不可能搞混。
第二个习惯是长期存在的 worktree 和临时 worktree 分开管理。比如一个持续维护的 feature 分支可能需要开几个星期的 worktree,而 review 分支可能只需要开一小时。后者用完立刻 git worktree remove,不让垃圾目录堆积。我会隔段时间跑一次 git worktree list,看看哪些已经不用的顺手清掉,保持仓库状态干净。这跟保持桌面整洁是一个道理——干净的环境出活更快,也出不了乱子。
第三个习惯是把 worktree 和当前任务绑定,而不是和分支绑定。意思是说,我开 worktree 的决策依据是“我现在需要同时做哪几件事”,而不是“我现在有哪几个分支”。如果你有五个分支但没有并行需求,那就不需要开五个 worktree;反过来,如果你需要同时做两件事但这两个改动都在同一个分支上,那也应该通过提交粒度来拆分,而不是靠开两个 worktree 来解决。worktree 解决的是并行的物理隔离,不是代码管理的逻辑拆分。
用 worktree 并行开发有一个挺微妙的体验变化:以前我觉得“换分支”是一件有仪式感的事,总要整理一下当前进度才敢切换;现在因为每个任务都有了自己的目录,切换的成本变得极低,随时可以放下一个任务去看另一个任务,心理负担小了很多。这个功能值得每一个被多任务切换折磨过的 Git 用户花几分钟试试,大概率会像我一样,回不去了。
