我之前切分支全靠 stash:改一半、存起来、切走、再切回来 pop,结果十次有八次 pop 出一地冲突,改的东西还得手动合并。后来知道有 worktree 这玩意儿——同一个仓库同时开好几个工作目录、各干各的分支互不干扰,才觉得自己以前是真傻。这篇就把它的基本用法和我的工作流记下来。
Git Worktree 教程
Git Worktree — 让你从同一个仓库同时检出多个工作目录,实现多分支并行开发
官方文档:https://git-scm.com/docs/git-worktree Git 官网:https://git-scm.com/
先交代下背景。我第一次被 worktree 救回来,是那次功能写一半,突然被喊去改线上 bug。切来切去、stash 来 stash 去,最后 pop 出一地冲突,差点想砸键盘。后来才知道 Git 本来就有 worktree 这个功能:同一个仓库开好几个工作目录,各干各的,互不打扰。这篇把我踩坑之后整理的用法从头到尾过一遍。
1. 什么是 Git Worktree
说白了,Git Worktree 就是允许你从同一个 Git 仓库中,在不同的目录里同时检出多个分支。每个目录称为一个 worktree(工作树),它们共享同一个 .git 数据库。
my-project/ ← 主工作树(main 分支)
├── .git/ ← 所有 worktree 共享这个 .git
├── src/
└── ...
../my-project-feature/ ← 另一个 worktree(feature 分支)
├── src/
└── ...
../my-project-hotfix/ ← 又一个 worktree(hotfix 分支)
├── src/
└── ...
核心概念
| 概念 | 说明 |
|---|---|
| 主工作树 (main worktree) | 仓库克隆时的默认工作目录 |
| 附加工作树 (linked worktree) | 通过 git worktree add 创建的额外工作目录 |
共享 .git | 所有 worktree 共享同一个对象数据库和 refs |
| HEAD 独立 | 每个 worktree 有自己独立的 HEAD,互不干扰 |
2. 为什么需要 Worktree
先说说我以前最头疼的一个场景:活儿没干完,又得切分支,来回折腾。
传统分支切换的痛点
# 传统方式:切换分支前必须处理未提交的修改
git stash # 1. 暂存修改
git switch feature-a # 2. 切换分支
# ... 工作 ...
git switch main # 3. 切回来
git stash pop # 4. 恢复修改(可能冲突!)
问题:
- 频繁切换分支时,stash/commit 的操作非常耗时
- 大型项目中
git checkout需要替换大量文件,速度慢 - stash pop 可能产生冲突,打断工作流
- 无法同时在多个分支上工作
Worktree 的优势
# 使用 worktree:并行开发,无需切换
git worktree add ../project-feature -b feature-a
git worktree add ../project-hotfix -b hotfix-b
# 两个目录可以同时打开在不同的 IDE 窗口中
# 修改互不影响,无需 stash
3. 基础操作
3.1 创建 Worktree
创建已有分支的 worktree
# 基本语法
git worktree add <path> <branch>
# 示例:为已有的 feature 分支创建 worktree
git worktree add ../my-project-feature feature/awesome-feature
创建新分支的 worktree
# 创建新分支并同时创建 worktree
git worktree add -b hotfix/login-bug ../my-project-hotfix
# 等价于:
# git worktree add ../my-project-hotfix -b hotfix/login-bug
从特定提交创建 worktree
# 检出某个特定的 commit(detached HEAD 状态)
git worktree add --detach ../my-project-review abc1234
# 从某个 tag 创建
git worktree add --detach ../my-project-v1.0 v1.0
3.2 列出所有 Worktree
git worktree list
输出示例:
/home/user/my-project abc1234 [main]
/home/user/my-project-feature def5678 [feature/awesome-feature]
/home/user/my-project-hotfix ghi9012 [hotfix/login-bug]
3.3 删除 Worktree
# 删除 worktree(推荐)
git worktree remove ../my-project-hotfix
# 强制删除(有未提交修改时)
git worktree remove --force ../my-project-hotfix
删除 worktree 后,对应的分支不会被删除,仍然保留在仓库中。
3.4 清理失效的 Worktree 引用
# 清理已不存在的 worktree 引用
git worktree prune
# 预览将被清理的内容(不实际执行)
git worktree prune --dry-run
4. 进阶用法
4.1 锁定 Worktree
锁定可以防止 worktree 被意外清理或 prune:
# 锁定 worktree
git worktree lock ../my-project-feature
# 给锁添加备注
git worktree lock --reason "长期分支,勿清理" ../my-project-feature
# 解锁
git worktree unlock ../my-project-feature
4.2 创建独立于任何分支的 Worktree(Detached HEAD)
# 从特定 commit 创建 detached worktree
git worktree add --detach ../review-branch HEAD~5
# 适合用于代码审查,不关联任何分支
在 detached HEAD 的 worktree 中,你可以:
- 测试特定版本的代码
- 阅读历史提交的代码
- 基于该 commit 创建新分支(
git checkout -b new-branch)
4.3 将 Worktree 移动到新位置
# 移动 worktree
git worktree move ../old-path ../new-path
4.4 查看 Worktree 的详细信息
# 查看当前 worktree 的主仓库路径
git worktree list --porcelain
输出示例:
worktree /home/user/my-project
HEAD abc1234567890abcdef1234567890abcdef123456
branch refs/heads/main
worktree /home/user/my-project-feature
HEAD def5678901234567890abcdef1234567890abcdef
branch refs/heads/feature/awesome-feature
4.5 在 Worktree 中创建新分支
# 先进入 worktree 目录
cd ../my-project-feature
# 创建并切换到新分支
git switch -c feature/new-part
# 或者回到主仓库,直接创建
git worktree add ../new-worktree -b feature/new-part
5. 实际工作流场景
5.1 场景一:开发中紧急修复线上 Bug
比如我正埋头在 feature/x 上写功能,线上突然报了个 bug 要马上修:
# 1. 不中断当前工作,直接创建 hotfix worktree
git worktree add ../my-project-hotfix -b hotfix/production-bug
# 2. 在新目录中修复 Bug
cd ../my-project-hotfix
vim src/bugfix.py
git add . && git commit -m "fix: production bug"
# 3. 推送并创建 PR
git push origin hotfix/production-bug
# 4. 回到原来的目录继续开发(无需任何 stash 操作)
cd ../my-project-feature
# 继续原来的工作...
5.2 场景二:同时审查多个 PR
# 为每个 PR 创建独立的 worktree
git worktree add ../review-pr-42 origin/feature/pr-42
git worktree add ../review-pr-58 origin/feature/pr-58
# 在各自的目录中审查代码
cd ../review-pr-42
# 启动 IDE 审查...
cd ../review-pr-58
# 启动另一个 IDE 审查...
# 审查完毕后清理
git worktree remove ../review-pr-42
git worktree remove ../review-pr-58
5.3 场景三:同时开发前端和后端
# 假设 main 分支包含全栈代码
# 创建前端和后端的独立 worktree
git worktree add ../my-project-frontend -b feature/frontend-redesign
git worktree add ../my-project-backend -b feature/api-v2
# 用不同的 IDE 打开不同的目录
# 前端修改不会影响后端,反之亦然
5.4 场景四:对比不同版本的代码
# 创建两个 worktree,分别检出不同版本
git worktree add --detach ../compare-v1 v1.0.0
git worktree add --detach ../compare-v2 v2.0.0
# 使用 diff 工具对比
diff -r ../compare-v1 ../compare-v2
# 对比完毕后清理
git worktree remove ../compare-v1
git worktree remove ../compare-v2
5.5 场景五:在 CI/CD 中并行测试多个分支
#!/bin/bash
# CI 脚本示例
branches=("main" "feature/a" "feature/b")
for branch in "${branches[@]}"; do
workdir="../test-$branch"
git worktree add "$workdir" "$branch"
cd "$workdir"
uv sync && uv run pytest
cd -
git worktree remove "$workdir"
done
6. 限制与注意事项
6.1 同一分支不能被两个 Worktree 同时检出
# ❌ 错误:main 分支已经在主工作树中检出了
git worktree add ../another-main main
# error: 'main' is already checked out at '/home/user/my-project'
解决方案: 创建一个新分支
git worktree add ../another-main -b main-copy
6.2 子模块(Submodule)限制
# 子模块不会在新 worktree 中自动初始化
# 需要手动执行:
cd ../new-worktree
git submodule init
git submodule update
这是一个已知的限制。每个 worktree 需要独立初始化子模块。
6.3 Git Hooks 行为
- 只有主工作树(主克隆目录)会执行
.git/hooks中的 hook - 附加 worktree 的
.git文件只是一个指向主仓库的引用,不会触发 hook
解决方案: 使用 core.hooksPath 为每个 worktree 指定 hook 路径:
cd ../my-worktree
git config core.hooksPath .hooks
6.4 磁盘空间
每个 worktree 都会检出完整的文件副本,会占用额外的磁盘空间。但它们共享 .git 对象数据库,所以比克隆整个仓库要节省空间。
6.5 IDE / 编辑器配置
大多数 IDE(VS Code、JetBrains 系列)可以同时打开多个 worktree 目录。每个 worktree 可以在独立的 IDE 窗口中打开。
7. 常见问题
Q: Worktree 和 git clone 有什么区别?
| 对比 | git worktree add | git clone |
|---|---|---|
.git 数据库 | 共享 | 独立复制 |
| 磁盘空间 | 较小 | 较大 |
| 网络要求 | 不需要 | 需要 |
| 推送/拉取 | 在主仓库操作 | 各自独立 |
| 适合场景 | 同一项目的并行开发 | 全新项目 |
Q: 如何将 Worktree 的修改合并回主分支?
# 在 worktree 中正常开发并提交
cd ../my-project-feature
git add . && git commit -m "feat: new feature"
# 回到主工作树
cd ../my-project
# 合并 worktree 的分支
git merge feature/awesome-feature
# 或者推送并创建 PR
git push origin feature/awesome-feature
Q: 删除 Worktree 后分支会丢失吗?
不会。git worktree remove 只删除工作目录,分支和提交历史都保留在仓库中。你可以随时通过 git switch feature/awesome-feature 检出它。
Q: 如何查看某个 Worktree 中的未提交修改?
# 查看所有 worktree 的状态
for wt in $(git worktree list --porcelain | grep "^worktree " | awk '{print $2}'); do
echo "=== $wt ==="
git -C "$wt" status --short
done
Q: Worktree 支持 worktree 里面再创建 worktree 吗?
不推荐。嵌套 worktree 可能导致路径和 .git 引用混乱。始终从主仓库目录创建 worktree。
8. Worktree vs 其他方案对比
| 特性 | git worktree | git switch | git clone | docker 容器 |
|---|---|---|---|---|
| 并行开发 | ✅ | ❌ | ✅ | ✅ |
| 共享对象数据库 | ✅ | ✅ | ❌ | ❌ |
| 磁盘开销 | 中 | 无 | 高 | 高 |
| 切换速度 | 即时(已检出) | 慢(文件替换) | 不适用 | 启动慢 |
| 需要 stash | ❌ | ✅ | ❌ | ❌ |
| 设置复杂度 | 低 | 低 | 低 | 高 |
| 适合场景 | 多分支并行 | 单分支切换 | 新环境 | 完全隔离 |
9. 常用命令速查表
创建
| 命令 | 说明 |
|---|---|
git worktree add <path> <branch> | 为已有分支创建 worktree |
git worktree add -b <name> <path> | 创建新分支并创建 worktree |
git worktree add --detach <path> <commit> | 从特定 commit 创建(detached HEAD) |
管理
| 命令 | 说明 |
|---|---|
git worktree list | 列出所有 worktree |
git worktree list --porcelain | 以机器可读格式列出 |
git worktree move <old> <new> | 移动 worktree 到新位置 |
git worktree lock <path> | 锁定 worktree |
git worktree unlock <path> | 解锁 worktree |
清理
| 命令 | 说明 |
|---|---|
git worktree remove <path> | 删除 worktree |
git worktree remove --force <path> | 强制删除(含未提交修改) |
git worktree prune | 清理失效的 worktree 引用 |
git worktree prune --dry-run | 预览将被清理的内容 |
其他
| 命令 | 说明 |
|---|---|
git worktree list --porcelain | 查看 worktree 详细信息 |
git config core.hooksPath <path> | 为 worktree 指定 hook 路径 |
完整工作流示例
把我一天的典型流程串起来,大概就是下面这样(也是我现在每天都在用的):
# === 早上开始工作 ===
# 1. 查看当前 worktree 状态
git worktree list
# 2. 为今天的任务创建 worktree
git worktree add ../my-app-feature -b feature/new-dashboard
# === 在 worktree 中开发 ===
cd ../my-app-feature
# 用 IDE 打开这个目录
code .
# 开发、测试、提交
git add . && git commit -m "feat: add dashboard page"
# === 线上紧急 Bug ===
# 3. 不需要切换,直接创建 hotfix worktree
git worktree add ../my-app-hotfix -b hotfix/urgent-fix
cd ../my-app-hotfix
# 修复 Bug
git add . && git commit -m "fix: urgent production bug"
git push origin hotfix/urgent-fix
# === 回到正常工作 ===
# 4. 回到 feature worktree 继续开发
cd ../my-app-feature
# 继续之前的工作...
# === 完成任务 ===
# 5. 合并分支
git checkout main
git merge feature/new-dashboard
# 6. 清理 worktree
git worktree remove ../my-app-feature
git worktree remove ../my-app-hotfix 