遇到过这种情况吗?
正写着代码,突然产品经理跑过来说线上出bug了,得马上修。你看看自己的工作区:改了一半的文件、新加的测试代码、还有几个临时文件......这时候你会怎么办?
以前我都是 git stash,改完bug再 stash pop 回来。但有时候 stash 多了就乱了,pop 的时候还可能冲突。后来发现 git worktree 这个功能,简直完美解决了这个问题。
Worktree 是什么?
说白了就是让你在一个 Git 仓库里同时 checkout 多个分支到不同的文件夹。比如:
bash
project/ # 主目录,开发新功能
project-hotfix/ # 修bug用
project-review/ # 看别人代码用
这三个目录用的是同一个 Git 仓库,提交、分支这些都是共享的,但每个目录可以切到不同分支,互不干扰。
想了解更多细节?可以看看 Git 官方文档
常用命令详解
1. 添加工作区 (add)
基本语法:
css
git worktree add [选项] <路径> [<提交号>]
常见用法:
csharp
# 最简单:自动创建同名分支
git worktree add ../hotfix
# 会在 ../hotfix 目录创建名为 hotfix 的新分支
# 基于现有分支创建工作区
git worktree add ../feature-test feature-branch
# checkout 已存在的 feature-branch 分支
# 创建新分支
git worktree add -b new-feature ../new-feature
# 基于当前 HEAD 创建新分支
# 基于指定提交创建新分支
git worktree add -b bugfix-v1 ../bugfix v1.0.0
# 从 v1.0.0 标签创建 bugfix-v1 分支
# 创建分离 HEAD(不关联任何分支)
git worktree add -d ../temp-check
# 适合临时查看代码,不打算提交
常用参数说明:
-b <新分支名>:创建新分支并 checkout-B <分支名>:强制创建分支,如果已存在则重置它-d或--detach:创建分离 HEAD 状态的工作区-f或--force:强制操作,即使分支已在其他工作区被 checkout--lock:创建后立即锁定工作区,防止被删除或移动--orphan:创建孤儿分支(空历史的新分支)
智能分支追踪: 如果你指定的分支在本地不存在,但远程有同名分支,Git 会自动帮你创建并设置追踪:
bash
git worktree add ../feature-auth feature-auth
# 如果本地没有 feature-auth,但 origin/feature-auth 存在
# 会自动创建本地分支并追踪远程分支
2. 查看工作区列表 (list)
基本语法:
css
git worktree list [选项]
示例:
shell
# 基本列表
git worktree list
# 输出:
# /path/to/project abc1234 [main]
# /path/to/project-hotfix def5678 [hotfix-bug]
# /path/to/temp 789abc0 (detached HEAD)
# 详细信息(显示锁定原因等)
git worktree list --verbose
# 输出:
# /path/to/project abc1234 [main]
# /path/to/locked-work def5678 [feature-a]
# locked: 在移动硬盘上
# /path/to/prunable 789abc0 (detached HEAD)
# prunable: gitdir 文件指向不存在的位置
# 机器可读格式(适合脚本解析)
git worktree list --porcelain
常用参数:
-v或--verbose:显示详细信息,包括锁定原因--porcelain:以固定格式输出,方便脚本解析-z:配合 --porcelain 使用,用 NUL 字符分隔(处理路径中包含换行的情况)
3. 删除工作区 (remove)
基本语法:
arduino
git worktree remove [选项] <工作区>
示例:
bash
# 删除干净的工作区
git worktree remove ../hotfix
# 强制删除(即使有未提交的更改)
git worktree remove -f ../hotfix
# 删除锁定的工作区(需要两次 --force)
git worktree remove --force --force ../locked-work
参数说明:
-f或--force:强制删除脏工作区(有未提交的修改)--force --force:强制删除锁定的工作区
4. 移动工作区 (move)
基本语法:
xml
git worktree move <工作区> <新路径>
示例:
bash
# 移动工作区到新位置
git worktree move ../hotfix ../urgent-fix
# 移动锁定的工作区(需要两次 --force)
git worktree move --force --force ../locked-work ../new-location
**注意:**主工作区和包含子模块的工作区不能移动。
5. 锁定/解锁工作区 (lock/unlock)
基本语法:
xml
git worktree lock [选项] <工作区>
git worktree unlock <工作区>
示例:
bash
# 锁定工作区并说明原因
git worktree lock ../portable-work --reason "在移动硬盘上"
# 解锁工作区
git worktree unlock ../portable-work
使用场景: 工作区在移动硬盘、U盘或网络盘上,不希望被自动清理时使用。
6. 修复工作区 (repair)
基本语法:
css
git worktree repair [<路径>...]
示例:
bash
# 在主工作区修复所有链接
git worktree repair
# 修复特定工作区(如果手动移动了目录)
git worktree repair /new/path/to/moved-worktree
# 修复多个工作区
git worktree repair ../work1 ../work2 ../work3
使用场景:
- 手动移动了工作区目录
- 手动移动了主仓库
- Git 连接损坏需要重建
7. 清理过期记录 (prune)
基本语法:
css
git worktree prune [选项]
示例:
bash
# 清理已删除工作区的记录
git worktree prune
# 预览会删除什么(不实际删除)
git worktree prune -n
# 显示详细清理过程
git worktree prune -v
# 清理超过指定时间未使用的工作区
git worktree prune --expire 7.days.ago
参数说明:
-n或--dry-run:只显示会删除什么,不实际删除-v或--verbose:显示详细信息--expire <时间>:只清理超过指定时间的记录
使用场景: 如果你直接用 rm -rf 删除了工作区目录(不推荐),用这个命令清理 Git 中的残留记录。
真实使用场景
场景一:紧急修bug
bash
# 赶紧创建个新目录改bug
git worktree add ../hotfix main
cd ../hotfix
# 改代码,提交
git commit -am "修复登录问题"
git push
# 回到原来的目录继续写代码
cd -
# bug修完了,删掉这个临时目录
git worktree remove ../hotfix
整个过程原来的工作区一点没动,改了一半的代码还在那儿。
场景二:Code Review
要看同事的代码,但不想影响自己正在写的:
bash
git worktree add ../review-pr feature/user-login
cd ../review-pr
# 慢慢看代码,跑跑测试
看完直接删掉就行,不用来回切分支。
场景三:同时开发多个功能
css
git worktree add ../feature-a -b feature-a
git worktree add ../feature-b -b feature-b
写着写着 feature-a 卡住了?直接去 feature-b 目录写别的,不用切来切去。
场景四:测试不同版本
bash
# 测试当前版本
git worktree add -d ../test-current
# 测试旧版本
git worktree add -d ../test-v1 v1.0.0
# 测试完删掉
git worktree remove ../test-current
git worktree remove ../test-v1
一些坑和注意事项
同一个分支不能在多个工作区同时 checkout
会报错。想想也合理,不然容易乱。非要这么干的话加 --force,但我从没这么用过。
直接删文件夹不行
别直接 rm -rf 删工作区目录,用 git worktree remove。否则 Git 里还留着记录,虽然可以用 git worktree prune 清理,但还是麻烦。
子模块支持不太好
文档里明说了,有子模块的项目最好别用 worktree。我试过几次确实有点问题。
移动目录后要修复
如果手动把工作区目录挪了地方,记得运行:
git worktree repair
主工作区不能删除
git worktree remove 只能删除链接工作区,主工作区(最初 clone 或 init 的那个)删不了。
小技巧
目录名起得有意义点
别都叫 temp1、temp2,过两天就忘了哪个是哪个。我一般这样:
sql
git worktree add ../fix-issue-123 -b fix-issue-123
git worktree add ../feature-user-auth -b feature-user-auth
定期清理
偶尔跑一下 git worktree list,看看有没有不用的工作区。该删的删掉,保持干净。
用相对路径还是绝对路径?
看个人习惯。我一般用相对路径 ../xxx,这样整个项目目录移动也不影响。如果需要绝对路径,可以在配置中设置:
arduino
git config worktree.useRelativePaths false
创建时就锁定
如果知道工作区在移动设备上,创建时直接锁定:
csharp
git worktree add --lock ../portable-work --reason "在U盘上"
什么时候用 Worktree?
适合用:
- 需要紧急切换任务
- 同时开发多个功能
- Code Review 不想影响当前工作
- 想在不同分支间对比代码
- 测试不同版本的代码
不适合用:
- 项目有子模块
- 只是临时看看代码(直接
git show或者 GitHub 上看就行) - 磁盘空间紧张(每个工作区都要占空间)
总结
Worktree 这功能其实很早就有了,但知道的人不多。用习惯之后真的回不去了,尤其是经常被打断改 bug 的时候。
不过也别滥用,大部分情况下一个工作区够用了。真需要的时候,Worktree 能帮你省不少事儿。
想深入了解所有选项和高级用法,建议看看 Git 官方文档的 Worktree 章节,里面讲得很详细。
快速参考
csharp
# 创建工作区
git worktree add <路径> # 自动创建同名分支
git worktree add <路径> <分支> # 基于现有分支
git worktree add -b <新分支> <路径> # 创建新分支
git worktree add -d <路径> # 分离HEAD
# 查看和管理
git worktree list # 查看所有工作区
git worktree list -v # 详细信息
git worktree remove <路径> # 删除工作区
git worktree prune # 清理记录
# 锁定和修复
git worktree lock <路径> # 锁定
git worktree unlock <路径> # 解锁
git worktree repair # 修复连接