惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

爱范儿
爱范儿
Y
Y Combinator Blog
博客园 - Franky
D
Docker
B
Blog RSS Feed
M
MIT News - Artificial intelligence
雷峰网
雷峰网
博客园 - 司徒正美
人人都是产品经理
人人都是产品经理
宝玉的分享
宝玉的分享
S
SegmentFault 最新的问题
GbyAI
GbyAI
Recent Announcements
Recent Announcements
Martin Fowler
Martin Fowler
H
Hackread – Cybersecurity News, Data Breaches, AI and More
MyScale Blog
MyScale Blog
B
Blog
H
Help Net Security
Microsoft Security Blog
Microsoft Security Blog
WordPress大学
WordPress大学
Vercel News
Vercel News
The Cloudflare Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Google DeepMind News
Google DeepMind News

木灵鱼儿

18 NestJS 基于 Docker 的 NestJS + Prisma + Playwright 生产环境容器化实践 - 木灵鱼儿 17 NestJS 使用 CoreModule 实现基础设施与业务域的彻底解耦 - 木灵鱼儿 VS Code BYOK 配置生成器 - 木灵鱼儿 2026 最新禁止浏览器密码管理器弹出教程 - 木灵鱼儿 16 NestJS 企业级 RBAC 权限控制体系 - 木灵鱼儿 15 NestJS 统一响应体设计(信封模式) - 木灵鱼儿 14 NestJS 生产级错误过滤方案 - 木灵鱼儿 13 NestJS 集成 TypeORM 完全指南 - 木灵鱼儿 12 NestJS 集成 Prisma ORM 完全指南(Prisma v7) - 木灵鱼儿 11 NestJS 注册与登录接口的密码安全设计 - 木灵鱼儿 10 NestJS JWT 身份验证完全指南 - 木灵鱼儿 09 NestJS 使用 @nestjs-swagger 生成 API 文档 - 木灵鱼儿 08 NestJS DTO 校验、Entity 脱敏与 Mapped Types 实战(TypeORM & Prisma) - 木灵鱼儿 07 NestJS 使用Caching缓存(cache-manager) - 木灵鱼儿 06 NestJS 使用Redis - 木灵鱼儿 05 Nestjs 高性能构建之SWC与Typescript7选型 - 木灵鱼儿 04 Nestjs API版本控制策略 - 木灵鱼儿 NestJS 正确处理日志 - 木灵鱼儿 NestJS config模块扩展用法.md - 木灵鱼儿 宝塔如何给网站一次性绑定两个域名 - 木灵鱼儿 NestJS 环境变量与配置管理(Config 模块) - 木灵鱼儿 如何使用 FNM 自动切换不同项目的 Node.js 版本 - 木灵鱼儿 如何使用 Node.js Corepack 自动锁定和切换项目包管理器版本 - 木灵鱼儿 日常开发中的 void:从忽略 Promise 到理解 JavaScript 历史写法 - 木灵鱼儿 了解并解决 Git 大小写“无感”的幽灵 Bug - 木灵鱼儿 从零开始:手把手教你封装一个企业级 Axios 请求模块 - 木灵鱼儿 03-Vue Query 高级进阶:应对复杂业务场景的硬核套路 - 木灵鱼儿 02-Vue Query 快速入门:从零构建你的第一个声明式查询 - 木灵鱼儿 01-异步状态管理新范式:为什么在 Vue 3 中使用 vue-query? - 木灵鱼儿 git 如何将所有历史提交合并为一条 - 木灵鱼儿
git 进阶指南:如何用 Git Worktree 完美应对紧急 Bug 与多分...
木灵鱼儿 · 2026-07-03 · via 木灵鱼儿

前言

最近,随着 Codex、VSCode的Agents模式、Claude Desktop 等 AI 编程辅助工具的火爆,worktree(工作树)这个功能开始频繁出现在开发者的视野中。

其实,git worktree 并非新事物,它是 Git 2.5(2015 年)就引入的“神兵利器”。本文将用最通俗的语言,带你彻底搞懂这个能让开发效率翻倍的“高阶操作”。


为什么要用 Git Worktree?

在日常开发中,你一定遇到过以下三个“破防瞬间”

痛点一:紧急修 Bug,代码写了一半无法 commit

  • 日常场景:你正在 feature 分支写新功能,代码改得乱七八糟,连编译都通不过。突然接到紧急线上 Bug,需要切到 main 分支去修复。
  • 传统做法:使用 git stash 暂存代码 → 切到 main → 修 Bug → 切回 featuregit stash pop
  • 痛点:步骤繁琐,稍有不慎遇到冲突,或者不小心丢了 stash,心态直接崩溃。
  • ✨ Worktree 方案:保持当前目录一动不动。在旁边“一秒”创建一个独立的 bugfix 文件夹,修完提交。主目录的代码依然完好无损。

痛点二:无法同时运行/对比两个分支

  • 日常场景:你想同时启动 main(旧版)和 feature(新版)的本地服务,对比它们的接口数据或 UI 表现。
  • 传统做法:由于一个本地目录同一时间只能检出(checkout)一个分支,你无法在同一个文件夹里运行两个版本。
  • ✨ Worktree 方案:左边屏幕用 VS Code 打开 A 文件夹(跑 8080 端口),右边屏幕打开 B 文件夹(跑 8081 端口),轻松实现双开联调

痛点三:大项目频繁切分支,导致编译缓存失效

  • 日常场景:在大型项目(如大型前端、Go 或 Rust 项目)中,每次 git checkout 分支,由于大量文件发生变化,导致编译器的本地缓存全部失效
  • 传统做法:每次切分支,都要经历漫长的 npm installgo buildcargo build,白白浪费数分钟。
  • ✨ Worktree 方案:每个工作区文件夹独立保留各自的依赖包和编译缓存,互不干扰。

核心对决:Checkout vs Clone vs Worktree

评估维度普通切分支 (git checkout)复制新仓库 (git clone)Git Worktree (推荐)
磁盘占用极小翻倍(重复下载所有 .git 历史)极小(共享同一个本地 .git 仓库)
多分支双开❌ 无法做到✅ 可以,但两个仓库数据不互通✅ 完美支持,本地分支与状态实时共享
未提交代码处理必须先 stashcommit无需处理无需处理,两边物理隔离,互不干扰
编译缓存/依赖频繁切换导致缓存失效、重装依赖独立缓存,但两边依赖完全不共享独立缓存,支持硬链接,省时省空间
创建速度瞬时极慢(受限于网速和仓库体积)瞬时(毫秒级)

深度思考:Worktree 真的能解决“依赖安装慢”的问题吗?

上面提到 Worktree 可以避免频繁切换分支导致的缓存失效,但请不要误以为它不需要安装依赖

既然每个 Worktree 都是一个独立的文件夹,首次进入该文件夹时,安装依赖是不可避免的。但它的核心优势在于:“一次安装,彻底隔离,避免往复折腾”

场景直观对比

  • 使用普通切分支 (checkout)
    每次从 feature(有新依赖)切回 main(无新依赖)再切回去,你都需要来回运行 npm install 并等待编译器重新构建。切 10 次,你就要痛苦地等待 10 次。
  • 使用 Worktree

    • 文件夹 A (feature 分支):首次安装,耗时 3 分钟。
    • 文件夹 B (main 分支):首次安装,耗时 3 分钟。
    • 一旦两边首次编译完成,后续在两边切换是“秒级”的。你切换 100 次,也不需要再多花一秒钟去重装或重新编译。
    • 专业提示:现代包管理器(如 pnpmYarnGo module)在本地有全局缓存。在创建第二个 Worktree 并安装依赖时,它们会通过硬链接(Hard Link)瞬间完成安装,几乎不耗费额外的磁盘空间和时间。

Worktree 目录应该怎么存放?

⚠️ 避坑指南:绝对不要把新建的 Worktree 目录放到你当前 Git 仓库的内部(即不要套娃),否则会导致 Git 追踪混乱。

建议在本地建立一个统一的 workspace(工作空间)目录,让它们并列排布:

workspace/ (统一父目录)
  ├── my-app/          # 主目录(平时写代码的地方,通常停留在 main 分支)
  ├── my-app-bugfix/   # 临时创建的工作区 1 (用于紧急修 Bug)
  └── my-app-feature/  # 临时创建的工作区 2 (用于开发新功能)

实战教学:真实项目下的 Worktree 全生命周期

现在,假定你已经在主目录 ~/workspace/my-app 下。

1. 创建 Worktree 阶段

情况 A:本地没有分支,需要“新建分支”并开发

你想基于 main 分支创建一个叫 feature-user 的新分支,并把工作区放在 ../my-app-user

# 语法:git worktree add -b <新分支名> <新目录路径> <起点分支>
git worktree add -b feature-user ../my-app-user main

情况 B:已有本地/远程分支,需要“关联并切过去”

同事已经把 feature-pay 分支推到了云端,你想在本地建一个工作区来联调。

git fetch # 先拉取最新分支信息
# 语法:git worktree add <新目录路径> <已有分支名>
git worktree add ../my-app-pay feature-pay

情况 C:不想要分支,只想快速查看某个历史提交(Detached HEAD)

有时候你只是想看看某次提交(比如 Commit ID 填 a1b2c3d)时的项目状态,不想建分支。

git worktree add --detach ../my-app-temp a1b2c3d
🔍 小贴士:想知道本地目前有哪些工作区正在运行?
运行命令:git worktree list

2. 开发与上传阶段

在新建的 Worktree 目录(如 ../my-app-user)中,你完成了代码编写。

请记住:git worktree 所有的操作完全是本地的。云端并不知道你搞了多个工作区。

要上传代码,你需要像往常一样,进入该工作区目录进行推送:

cd ../my-app-user
git add .
git commit -m "feat: 完成用户中心开发"
git push origin feature-user # 此时,分支和代码才正式上传到云端

3. 合并分支阶段

当开发完毕且代码推送到云端后,如何合并到 main 分支?

途径一:云端合并(团队规范推荐 🌟)

直接在 GitHub/GitLab 网页上提一个 PR (Pull Request)MR (Merge Request)

  • 优势:可以触发 CI/CD 自动化测试,方便同事进行 Code Review,点击网页上的 Merge 按钮即可,最安全。

途径二:本地合并(个人项目或快速迭代)

如果你想在本地手动合并,必须回到主目录(因为主目录才是本地 main 分支的所在地):

cd ~/workspace/my-app
git checkout main
git pull origin main # 保持代码最新

此时,Git 提供了不同的合并策略,你可以根据团队规范选择:

1. 默认 Fast-Forward (无分支痕迹)
   main ───●───●───● (历史记录呈一条直线)

2. --no-ff 合并 (保留分支历史,清晰推荐 🌟)
   main ───●─────────● (有一条清晰的分叉合并弧线)
            \       /
             ●───●─/  (feature-user)

3. Squash 合并 (干净,适合临时提交极多的情况)
   main ───●───● (把 feature 分支的多个 commit 压缩为一个大 commit)
  • 策略一:使用 --no-ff 合并(最推荐)

    git merge --no-ff feature-user -m "merge: 合并用户中心分支"

    效果:会在 Git 历史图谱中留下一条清晰的分叉又合并的“耳朵线”,即便以后删除了分支,合并记录也永久保留,方便追溯。

  • 策略二:使用 --squash 合并

    git merge --squash feature-user
    git commit -m "feat: 完整实现用户中心功能"

    效果:把你在开发分支上的所有零碎 Commit(如 "fix typo", "change UI")打包压缩成一个干净的 Commit 放入 main


4. 善后清理(如何安全删除 Worktree)

合并完成后,临时的 ../my-app-user 文件夹功成身退。

⚠️ 安全警告千万不要直接用 rm -rf 或者右键删除文件夹!
如果直接删除物理文件夹,Git 仓库内部仍然会残留该工作区的注册信息。请遵循以下标准步骤:
  1. 回到主目录

    cd ~/workspace/my-app
  2. 使用 Git 安全移除工作区(这会自动删除物理文件夹并注销注册信息):

    git worktree remove ../my-app-user
  3. 安全删除本地分支

    git branch -d feature-user

总结

  1. Worktree 的本质:一个本地 .git 仓库,物理上多开文件夹,实现“多线程”开发不卡顿。
  2. 创建工作区git worktree add [-b 新分支名] <新路径> [起点分支]
  3. 日常开发:进入对应的 Worktree 目录,照常进行 addcommitpush
  4. 分支合并:回到主目录,git checkout main,再用 git merge --no-ff 进行合并。
  5. 过河拆桥(清理)git worktree remove <路径>,干净利落,不留一丝痕迹!