用 Git Worktree 同时开多个需求,不用来回 stash

用 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 了。

相关推荐
IT_陈寒2 小时前
Vue的v-for为什么不加key也能工作?我差点翻车
前端·人工智能·后端
小碗羊肉2 小时前
【JavaWeb | 第十二篇】项目实战——登录功能
java·前端·数据库
一个处女座的程序猿O(∩_∩)O2 小时前
如何保持nginx配置与前端打包dist的路径保持一致、解决页面刷新白屏以及页面跳转问题
运维·前端·nginx
十有八七2 小时前
AI 开发,本质是一场文档的生命周期管理
前端·人工智能
Hyyy3 小时前
普通前端自救记录——第0周
前端
前端若水3 小时前
在 Vue 2 与 Vue 3 中使用 markdown-it-vue 渲染 Markdown 和数学公式
前端·javascript·vue.js
之歆3 小时前
DAY_10 JavaScript 深度解析:原型链 · 引用类型 · 内置对象 · 数组方法全攻略(下)
开发语言·前端·javascript·ecmascript
行星飞行4 小时前
从 cursor 、 Claude code 迁移到 codex,30 分钟快速上手 codex 常用技巧
前端
Pu_Nine_94 小时前
前端埋点从入门到企业实践:手写一个Demo + 主流方案对比
前端·埋点