Reading

git worktree:同时改两个分支,不再 stash 来 stash 去

改到一半被打断去修线上 bug,stash 来 stash 去容易丢上下文。git worktree 让同一个仓库同时检出多个分支,各占一个目录,切任务变成换个目录。

返回归档
git worktree:同时改两个分支,不再 stash 来 stash 去 封面图

适用场景:正在开发一个功能,改了一半,突然要切去修线上 bug。git stash 来回倒腾容易丢上下文,clone 第二份仓库又浪费磁盘还要重配环境。git worktree 让同一个仓库同时检出多个分支,各占一个目录,互不打扰。

背景和思路

我以前的做法是遇到紧急修复就 git stash,切分支,修完再切回来 git stash pop。问题在于 stash 是个栈,塞了两三个之后就不记得哪个是哪个了;而且没提交的新文件、改到一半的数据库 schema,pop 回来经常一身冲突。

git worktree 的思路完全不同:一个仓库可以有多个工作目录,每个目录检出不同的分支,共享同一个对象库。切任务变成了「换个目录」,而不是「换个世界」。

基本用法

假设仓库在 ~/work/project/QSWNotes,现在要基于 main 修一个线上问题:

cd ~/work/project/QSWNotes
git worktree add ../QSWNotes-hotfix -b fix/cover-overflow main

这条命令做了三件事:

  1. 在同级目录创建 QSWNotes-hotfix/
  2. 基于 main 建了新分支 fix/cover-overflow
  3. 把新分支检出到那个目录。

现在开两个终端(或者编辑器开两个窗口),一边继续写功能,一边修 bug,谁也不影响谁。修完在 hotfix 目录里正常 commit、push,完全是常规流程。

查看当前所有工作树:

git worktree list
/Users/qsw/work/project/QSWNotes           2a340b6 [main]
/Users/qsw/work/project/QSWNotes-hotfix    9f21c07 [fix/cover-overflow]

用完清理

分支合并之后,把工作树摘掉:

git worktree remove ../QSWNotes-hotfix

如果目录被手动删过,元数据会残留,用 prune 清理:

git worktree prune

几个规则和坑

同一个分支不能同时检出到两个工作树。 试图在第二个工作树里 checkout 一个已被占用的分支会直接报错,这是保护而不是限制——两边同时提交会把分支指针搞乱。

每个工作树的运行环境是独立的。 node_modules.env、构建缓存都不会共享,新工作树要重新 npm install。这既是成本(多占点磁盘)也是好处(两边可以同时起 dev server,端口不同即可)。

子目录里的 .git 是个文件不是目录。 新工作树里的 .git 只是一个指回主仓库的指针文件,某些较老的工具如果假设 .git 一定是目录,可能会出问题,遇到怪事先想到这一层。

磁盘开销远小于重新 clone。 对象库是共享的,新工作树只多出一份检出的工作区文件,历史记录不会复制。

我固定下来的工作流

  1. 主目录永远停在 main,保持干净,只用来拉取和看代码。
  2. 每个进行中的任务一个工作树,目录名带上任务语义:../QSWNotes-studio-v2../QSWNotes-hotfix
  3. 任务合并后立刻 git worktree remove,控制同时存在的工作树不超过三个。
  4. 配一个 shell 别名快速跳转:
alias gwl='git worktree list'

验证

想确认对象库确实共享、没有重复占用磁盘:

cat ../QSWNotes-hotfix/.git
gitdir: /Users/qsw/work/project/QSWNotes/.git/worktrees/QSWNotes-hotfix

指针指回主仓库,说明历史数据只有一份。

收尾检查

  • 主工作目录保持在 main,不在里面直接开发。
  • 工作树目录建在仓库外面(../),避免被当成仓库内容误提交。
  • 合并后的工作树及时 remove,定期 git worktree prune
  • 新工作树记得单独装依赖、配 .env