一、先聊聊那个让人崩溃的瞬间
来还原一个无比熟悉的场景:
你正在 feature/login 分支吭哧吭哧写了三个小时的代码,文件改了一半,控制台的 token 调试日志还没清理。突然,企业微信弹出:
测试 MM: "线上 main 有个紧急 bug,麻烦立刻修一下。"
这时候你只有三个选择:
-
git stash把活儿暂存起来 —— 等会儿回来 pop,遇到冲突血压升高 -
随便提交一个
WIP: 临时保存—— 把脏代码混进 git 历史 -
再 clone 一份仓库到别的目录 —— 浪费磁盘、配置环境、IDE 重新打开
有没有第四种?
有,而且是 Git 官方早在 2015 年(v2.5)就内置的功能:git worktree。可惜国内文章不多,很多人用了十年 git 都没听过。
二、git worktree 到底是个啥
一句话定义:
同一个 Git 仓库,可以同时拥有多个工作目录,每个目录检出不同的分支,互不干扰。
形象一点的对比:
|
传统方式 |
worktree 方式 |
|---|---|
|
一个仓库目录 → 同时只能看一个分支 |
一个仓库 → 多个目录 → 同时看多个分支 |
|
切分支前必须处理脏数据 |
各分支在各自目录,互不影响 |
|
想看两个分支只能 clone 两份 |
共用一份 .git,省磁盘省流量 |
目录结构大概长这样:
projects/
├── test-git/ ← 主工作树(main 分支)
│ ├── .git/ ← 真实的 git 数据库
│ └── 业务文件...
├── test-git-hotfix/ ← worktree 1(hotfix 分支)
│ ├── .git ← 注意:这是一个文件,不是目录
│ └── 业务文件...
└── test-git-feature/ ← worktree 2(feature 分支)
├── .git
└── 业务文件...
三个目录,共享同一份 git 历史和对象,只是工作区独立。
三、实战演示:用 wangx-wx/test-git 走一遍完整流程
下面所有命令都基于这个公开仓库,你可以一边读一边在自己电脑上跑:
https://github.com/wangx-wx/test-git.git
第一步:克隆仓库
git clone https://github.com/wangx-wx/test-git.git
cd test-git
第二步:看看现在的状态
# 当前在哪个分支
git branch
# 看所有远程分支
git branch -r
# 看所有 worktree(此刻只有主工作树)
git worktree list
输出大概是这样:
$ git worktree list
/Users/you/projects/test-git 3a8f9c2 [main]
只有一行,就是主工作树自己。
第三步:创建第一个 worktree —— 基于已存在的远程分支
假设我们要在不打断当前工作的前提下,去看一下 dev 分支的代码:
# 在 ../test-git-dev 目录创建工作树,并检出 dev 分支
git worktree add ../test-git-dev dev
执行后输出:
Preparing worktree (checking out 'dev')
HEAD is now at xxxxxx commit message
现在你的目录结构是:
projects/
├── test-git/ ← 还在 main,文件原封不动
└── test-git-dev/ ← 全新目录,里面是 dev 分支的代码
进去看一下:
cd ../test-git-dev
git branch # 显示 * dev
ls # 看到的是 dev 分支的文件
关键点: 主目录 test-git/ 里的脏代码完全不受影响。你可以随时 cd 回去继续之前的工作。
第四步:创建第二个 worktree —— 同时新建一个分支
紧急 bug 来了,需要基于 main 拉一个 hotfix 分支。一条命令搞定:
# 回到主目录
cd ../test-git
# -b 表示新建分支,名为 hotfix/login-crash
git worktree add -b hotfix/login-crash ../test-git-hotfix main
参数解读:
git worktree add -b hotfix/login-crash ../test-git-hotfix main
│ │ │ │
│ └── 新分支名 │ └── 起点(从哪儿拉)
└── "新建分支" 标志 └── worktree 路径
执行后:
projects/
├── test-git/ ← main 分支(脏代码还在)
├── test-git-dev/ ← dev 分支(看代码用)
└── test-git-hotfix/ ← hotfix/login-crash 分支(修 bug 用)
第五步:在 hotfix 目录里修 bug、提交、推送
cd ../test-git-hotfix
# 改文件...
git add .
git commit -m "fix: 修复登录崩溃问题"
# 推到远程
git push -u origin hotfix/login-crash
整个过程主目录的 feature/login 分支没动过一根毛。
第六步:查看所有 worktree
git worktree list
输出:
/Users/you/projects/test-git 3a8f9c2 [main]
/Users/you/projects/test-git-dev b1c2d3e [dev]
/Users/you/projects/test-git-hotfix f4e5d6c [hotfix/login-crash]
清清楚楚。
第七步:用完了,清理 worktree
bug 修完合并后,hotfix 这个 worktree 就不需要了:
# 在主目录里执行
cd ../test-git
# 删除 worktree(目录和工作区记录都会清理)
git worktree remove ../test-git-hotfix
# 如果分支也不要了,再删除分支
git branch -d hotfix/login-crash
如果 worktree 目录被你手动 rm -rf 删掉了,git 会留下失效引用,用这个清理:
git worktree prune
四、进阶必修课:本地分支 vs 远程分支
很多新人搞不清楚 dev 和 origin/dev 到底有啥区别,这里掰开揉碎讲。
4.1 三种"分支"的概念
|
分支类型 |
例子 |
在哪 |
谁动得了 |
|---|---|---|---|
| 本地分支 | dev |
你的电脑 |
你 |
| 远程跟踪分支 | origin/dev |
你的电脑(缓存) |
git fetch/pull 更新 |
| 远程分支 |
GitHub 上的 dev |
服务器 |
所有协作者 |
注意第二种:origin/dev存在你本地,但它是远程分支的"快照"。你不能直接 commit 到它上面,它只会被 git fetch 刷新。
可以用一张图理解:
[GitHub 服务器]
dev 分支 ──┐
│ git fetch / git pull
▼
[你的电脑 .git/]
origin/dev (远程跟踪分支,只读快照)
│ 关联
▼
dev (本地分支,你在上面工作)
4.2 怎么查看
# 只看本地分支
git branch
# 只看远程跟踪分支
git branch -r
# 全部看
git branch -a
# 看每个本地分支跟踪的是哪个远程分支
git branch -vv
git branch -vv 的输出示例:
* main 3a8f9c2 [origin/main] feat: 初始化项目
dev b1c2d3e [origin/dev: ahead 2] feat: 新功能 A
local-x f4e5d6c (无 [origin/xxx] = 没关联远程)
中括号里的就是它跟踪的远程分支。ahead 2 表示本地比远程多 2 个提交。
4.3 创建一个关联远程分支的本地分支 —— 四种方式
这是日常最常用的操作,按推荐顺序排:
方式 1:远程已存在,直接 checkout(推荐)
如果远程已经有 dev 分支,最简单:
git fetch origin # 先把远程信息拉到本地
git checkout dev # git 会自动建立跟踪关系
Git 2.23+ 也可以用更现代的 switch:
git switch dev
这两条命令在检测到 origin/dev 存在时,会自动创建本地 dev 分支并设置跟踪。
方式 2:显式指定跟踪关系
git checkout -b dev origin/dev
# 或
git switch -c dev --track origin/dev
适合本地分支名想和远程不一样的场景:
# 本地叫 dev-local,跟踪远程的 dev
git checkout -b dev-local origin/dev
方式 3:先建本地分支,再关联远程
适合"我已经在本地建了分支,想推到远程并建立跟踪":
git checkout -b my-feature # 先建本地分支
# 写代码、提交...
git push -u origin my-feature # -u 等同于 --set-upstream
-u 是关键,它会把远程分支注册为本地分支的 upstream,以后 git pull / git push 都不用再指定远程名。
方式 4:已有的本地分支,想补关联
# 已经在 my-feature 分支
git branch --set-upstream-to=origin/my-feature
# 或简写
git branch -u origin/my-feature
4.4 验证跟踪关系
git branch -vv
看到本地分支后面有 [origin/xxx] 就说明关联成功。之后:
-
git pull等于git pull origin xxx -
git push等于git push origin xxx -
git status会告诉你 ahead/behind 多少 commit
五、worktree + 远程分支:组合技
把上面两个能力组合起来,能解锁更优雅的工作流。
场景:reviewer 要在不污染主分支的前提下,本地跑同事的 PR 分支
同事提了一个分支 feature/payment-v2,你要在本地跑起来看看。
传统做法: 当前分支 stash,checkout 过去,跑完再切回来 pop。
worktree 做法:
git fetch origin # 拉最新远程信息
git worktree add ../test-git-review feature/payment-v2 # 一行搞定
cd ../test-git-review
# 这就是 feature/payment-v2 的工作区
npm install # 装依赖、跑测试
npm run dev
# 看完直接删
cd -
git worktree remove ../test-git-review
主目录全程没受影响。
场景:同时跑两个分支做 A/B 对比
git worktree add ../test-git-v1 release/v1
git worktree add ../test-git-v2 release/v2
开两个终端,分别 cd 进去,启动各自的服务,可以并行对比行为差异。
六、命令速查表
|
任务 |
命令 |
|---|---|
|
新增 worktree(已有分支) | git worktree add <path> <branch> |
|
新增 worktree(新建分支) | git worktree add -b <new-branch> <path> <起点> |
|
列出所有 worktree | git worktree list |
|
删除 worktree | git worktree remove <path> |
|
清理失效引用 | git worktree prune |
|
查看本地分支 | git branch |
|
查看远程分支 | git branch -r |
|
查看跟踪关系 | git branch -vv |
|
检出远程分支到本地 | git checkout <branch>
或 |
|
推送并关联远程 | git push -u origin <branch> |
|
补关联远程 | git branch -u origin/<branch> |
|
拉取远程更新(不合并) | git fetch |
|
拉取并合并 | git pull |
七、几个一定要知道的坑
1. 同一个分支不能被两个 worktree 同时检出
git worktree add ../test-git-2 main
# fatal: 'main' is already checked out at '/Users/you/projects/test-git'
这是 git 的保护机制。要看同一分支,可以基于它新建一个:
git worktree add -b temp-main ../test-git-2 main
2. 不要用 rm -rf 直接删 worktree 目录
会留下失效的 worktree 记录。正确做法:
git worktree remove <path>
万一已经手动删了,补救:
git worktree prune
3. 主工作树不能被删除
主工作树是 .git 目录所在的那个目录,其他 worktree 都依赖它。删它等于砸了根基。
4. IDE 缓存可能闹脾气
VS Code、IntelliJ 在第一次进入 worktree 目录时,会重新建索引,可能会卡几秒。属于正常现象。
5. worktree 不适合超大型仓库吗?
恰恰相反 —— 越大的仓库越适合。因为 worktree 不会重复存 .git 对象,比 clone 两份省一大半磁盘。
八、写在最后
回到开头那个场景。学会 worktree 后,处理紧急 bug 的流程变成:
git worktree add -b hotfix/xxx ../repo-hotfix main
cd ../repo-hotfix
# 修 bug、提交、推送
cd -
# 主目录的脏代码还在原地等你
三十秒搞定。
git 早在十年前就给了我们这把好刀,只是太多人没翻到刀鞘那一页。希望这篇能让你以后少 stash 一点、少崩溃一点。
延伸阅读:
git worktree --help才是最权威的文档,命令选项比本文涵盖的多得多。示例仓库:
https://github.com/wangx-wx/test-git.git,可克隆下来自己跑一遍命令。

1318

被折叠的 条评论
为什么被折叠?



