用 Git Worktree 同时开多个需求,不用来回 stash
痛点
正在 feature-A 上写到一半,突然要修一个线上 hotfix------这种事每隔几天就来一次。
传统做法是 git stash,切分支,修完再切回来,stash pop。偶尔 stash 冲突了,或者忘了 pop,或者 pop 完发现代码不对------都是时间浪费。
另一种做法是再 clone 一份仓库。问题是磁盘占用翻倍,而且两份仓库的分支不同步,需要反复 fetch。
Git Worktree 是解决这个问题的正确方式。
它是什么
一句话:同一个 .git 仓库,同时检出多个分支到不同目录。
每个 worktree 有独立的工作区和暂存区,但共享同一份 Git 对象存储(objects、refs 等)。意思是:磁盘占用极小,分支之间实时同步,不需要额外的 clone。
目录结构实际上长这样
css
主仓库 .git/
├── objects/ ← 所有 worktree 共享,只存一份
├── refs/
└── worktrees/ ← worktree 的管理数据
├── feature-a/
│ ├── HEAD ← feature-A 的 HEAD 指向
│ └── index ← feature-A 的独立暂存区
└── hotfix/
├── HEAD
└── index
工作目录(各自独立):
~/project/ ← main 分支
~/project-feature-a/ ← feature-A(目录里的 .git 是个文件,不是目录)
~/project-hotfix/ ← hotfix
worktree 目录里的 .git 是个文件,内容只有两行:
javascript
gitdir: /path/to/main/.git/worktrees/feature-a
worktree: /path/to/project-feature-a
它只做一件事:告诉 Git 这个目录属于哪个仓库。
常用命令
bash
# 把已有分支检出到新目录
git worktree add ../project-feature-a feature-A
# 创建新分支并检出(一步到位,最常用)
git worktree add -b feature/login ../project-login main
# 以 detached HEAD 模式检出(只读场景用,比如 code review)
git worktree add --detach ../review-pr origin/colleague-feature
# 查看所有 worktree
git worktree list
# 删除 worktree
git worktree remove ../project-feature-a
# 强制删除(有未提交的改动时)
git worktree remove --force ../project-feature-a
# 直接 rm -rf 了 worktree 目录之后,要清理残留记录
git worktree prune
最后这条 prune 很容易忘。如果你手动删了 worktree 目录,Git 里还留着记录,下次创建同名 worktree 会报错,所以养成习惯:prune 跟着 remove 一起用。
实战场景
场景一:紧急 hotfix
bash
# 正在 feature-A 上开发,不碰当前代码
git worktree add -b hotfix/api-timeout ../project-hotfix main
cd ../project-hotfix
# 修 bug...
git add . && git commit -m "fix: api timeout 增加重试"
git push origin hotfix/api-timeout
# 提完 MR 就清理
cd ~/project
git worktree remove ../project-hotfix && git worktree prune
整个过程中 feature-A 的代码一个字节都没动。
场景二:并行开发多个需求
bash
git worktree add -b feature/login ../project-login main
git worktree add -b feature/dashboard ../project-dashboard main
git worktree add -b feature/settings ../project-settings main
三个终端窗口,三个目录,三个分支同时跑 npm run dev,互不干扰。
场景三:用 worktree 做 bisect
git bisect 需要不断切换 HEAD 到不同的提交,会弄脏工作目录。用 worktree 隔离:
bash
git worktree add ../bisect-test --detach HEAD
cd ../bisect-test
git bisect start
git bisect bad HEAD
git bisect good v1.0.0
# 找到问题提交后
git bisect reset
cd ~/project
git worktree remove ../bisect-test
AI 辅助开发时的关键作用
这部分是很多人忽略的。
用 Cursor、Copilot 或 WorkBuddy 这类 AI 工具时,上下文污染 是真实存在的问题。如果你在同一个 AI 会话里反复切换分支,AI 会逐渐搞不清楚当前在干什么,给出的建议会越来越离谱,甚至把 A 需求的代码改到 B 需求的分支上。
解决方法很直接:每个需求 = 一个 worktree = 一个独立的 AI 会话窗口。
具体操作:
bash
# worktree 创建完直接打开新的 Cursor 窗口
git worktree add -b feature/xxx ../project-xxx main
cursor ../project-xxx &
每个 Cursor/VS Code 窗口有独立的 AI 上下文,不会串。
为了效率,可以把这套操作写成 shell 函数,加到 ~/.zshrc:
bash
wt() {
local branch=$1
local base=${2:-main}
local proj=$(basename $(git rev-parse --show-toplevel))
local dir="$HOME/${proj}-${branch}"
git worktree add -b "$branch" "$dir" "$base" 2>/dev/null \
|| git worktree add "$dir" "$branch" 2>/dev/null \
|| { echo "创建失败"; return 1; }
cursor "$dir" &>/dev/null &
cd "$dir"
}
wtrm() {
local branch=$1
local proj=$(basename $(git rev-parse --show-toplevel))
local dir="$HOME/${proj}-${branch}"
local main=$(git worktree list | head -1 | awk '{print $1}')
cd "$main"
git worktree remove "$dir" --force 2>/dev/null
git worktree prune
}
wtl() {
git worktree list
echo ""
git worktree list | tail -n +2 | while read -r path branch _; do
echo "--- $branch ---"
git -C "$path" status -s | head -5
done
}
用法:
bash
wt feature-login # 创建并打开 Cursor
wt feature-dashboard
wt hotfix-timeout # 再来一个
wtl # 一眼看到所有 worktree 的改动状态
wtrm feature-login # 合并完就清理
踩过的坑
同一分支不能同时在两个 worktree 里检出
bash
git worktree add ../another main
# 报错:'main' is already checked out at '~/project'
解决方式:用 --detach,或者检出不同的分支。
worktree 目录不能放在仓库内部
bash
git worktree add ./sub feature-A # 报错
必须放在仓库外面:../project-feature-a。
直接删目录不清理记录
bash
rm -rf ../project-feature-a
git worktree list # 还显示这个 worktree,但路径已经不存在了
正确做法:git worktree remove 或者 rm 之后一定跟一条 git worktree prune。
子模块不会自动初始化
worktree 里的 submodule 需要手动处理:
bash
cd ../project-feature-a
git submodule update --init --recursive
Cursor 会对每个 worktree 重新建立索引
这对大型项目来说比较慢。如果 worktree 只是用来快速修一个 hotfix,可以不用 Cursor 打开,只用终端 + AI 命令行工具。
Worktree vs 其他方案
| Worktree | Clone 多份 | Stash + checkout | |
|---|---|---|---|
| 磁盘占用 | 极小 | 翻倍 | 零 |
| 分支同步 | 实时共享 | 需要 fetch | 共享但需切换 |
| 并行工作 | 支持 | 支持 | 不支持 |
| 上下文丢失风险 | 无 | 无 | 有 |
结论比较明确:并行开发用 worktree,单纯想完全隔离环境才用 clone。
一句话总结
git worktree add -b 分支名 ../目录名 main ------记住这条命令,以后不需要再来回 stash 了。