Git Worktree 与多分支并行开发实战指南
TL;DR - 工程摘要
本文讨论:用单分支克隆 + git worktree 在大仓库里做多分支并行开发,以及多仓库任务 workspace 的组织方式。
适用场景:
- 大仓库同时推进多个平台分支(iOS/Android/鸿蒙)或多个特性分支
- bugfix 和 feature 并行,不想被 checkout/stash 打断主现场
- 跨仓库问题排查,需要把客户端 + SDK + 协议仓集中到一处
- 给 AI 编程工具提供干净、隔离、可删除的上下文目录
不适用场景:
- 单文件小修,直接在主目录改更快
- git 入门教学(本文假设你已熟练使用 add/commit/push/branch)
- 需要把多个仓库合并成一个大仓库(那是 submodule/subtree 的场景)
核心结论:worktree = 同一个仓库多开几个独立目录,每个目录检出不同分支,.git 共享。大仓库先用 --single-branch 只拉主分支省空间,再按需 worktree 检出其他分支。单分支克隆的隐蔽坑是 git fetch 非默认分支不创建 origin/xxx,worktree 要用 FETCH_HEAD 而非 origin/xxx。多仓库排查用统一 workspace 目录 + git -C 免 cd,给 AI 隔离现场而非直接读主仓库。
复现环境:Git ≥ 2.20,macOS / Linux,git worktree 子命令需 2.15+。
大仓库做多分支并行开发,最痛的不是写代码,是切分支。你正在 trunk 上调一个 bug,改了一半,突然要去看 android/release-t5 上的线上问题。传统做法是 git stash → git checkout → 干活 → 切回来 → git stash pop,中间只要 stash 冲突或忘了 pop,现场就乱了。如果同时还要看 ios/release-t5、ohos/release-t5,反复 checkout 根本顶不住。
git worktree 解决的就是这个:让同一个仓库同时拥有多个独立工作目录,每个目录检出不同分支,互不干扰。你给每个分支开一个目录,cd 过去就是那个分支的现场,cd 回来还是原来的现场。没有 stash,没有切换,并行开发天然成立。
1. 为什么需要 worktree
先看传统 checkout 和 worktree 的对比:
| 维度 | 传统 checkout | 单分支克隆 + worktree |
|---|---|---|
| 克隆时间 | 拉全部分支,慢 | 只拉主分支,快 |
| 磁盘占用 | 全部分支对象 | 按需拉取 |
| 切换分支 | 需 commit/stash → checkout → 恢复 | 直接 cd 到对应目录 |
| 并行开发 | 不支持 | 天然支持 |
| 丢弃临时分支 | 需切回来再删 | git worktree remove 一键清理 |
核心理解一句话:worktree = 同一个仓库,多开几个独立目录,每个目录检出不同分支,.git 对象库共享。
这意味着任意一个 worktree 里 git fetch,其他 worktree 都能拿到新对象;但每个 worktree 的工作区、暂存区、当前分支是独立的。你可以在 A 目录跑测试,同时去 B 目录改另一个分支,互不影响。
2. 单分支克隆与 FETCH_HEAD 坑
单分支克隆:大仓库的起点
对于超大型仓库(合仓后的客户端、多媒体引擎等),完整克隆会拉下所有分支的全部历史,又慢又占空间。单分支克隆只拉你关心的那一条分支:
# 只拉 trunk 分支,省空间省时间
git clone --single-branch -b trunk <repo-url> ~/trunk_file/client
# 按需为其他分支创建 worktree
cd ~/trunk_file/client
git fetch origin android/feature-x
git worktree add ~/trunk_file/client-android FETCH_HEAD日常目录布局惯例:
~/trunk_file/
├── client/ # 主仓库(trunk,保持干净)
├── client-android/ # Android 分支 worktree
├── client-ios/ # iOS 分支 worktree
├── client-ohos/ # 鸿蒙分支 worktree
└── client-feature-x/ # 临时特性分支 worktree主仓库保持干净,随时 git pull 同步;各 worktree 独立 commit / rebase / push,互不影响。
FETCH_HEAD 坑(单分支克隆专属)
这是单分支克隆最隐蔽的坑,99% 的 git 教程不会讲,但踩一次就记一辈子。
单分支克隆的 fetch refspec 通常只追踪默认分支:
# .git/config 里的 refspec,只追踪 master
+refs/heads/master:refs/remotes/origin/master所以当你 git fetch origin android/release-t5 时,数据是拉下来了,但不会创建 origin/android/release-t5 这个远程引用。如果你接着用 origin/android/release-t5 作为 worktree 的基提交,会报错说不存在:
# ❌ 错误:单分支仓库 fetch 非默认分支不会创建远程引用
git fetch origin android/release-t5
git worktree add -b android/release-t6 ~/trunk_file/client-android origin/android/release-t5
# fatal: invalid reference: origin/android/release-t5
# ✅ 正确:用 FETCH_HEAD
git fetch origin android/release-t5
git worktree add -b android/release-t6 ~/trunk_file/client-android FETCH_HEAD规律:从默认分支(master/trunk)切 worktree,origin/master 始终可用,没问题;从非默认分支切,必须 fetch 后用 FETCH_HEAD。
查远端有哪些分支,单分支仓库本地没有完整记录,用 git ls-remote 直接查远端:
# 查远端所有 ios 发布分支
git ls-remote --heads origin 'refs/heads/*/ios_*'
# 只取分支名(去掉 commit hash 和 refs/heads/ 前缀)
git ls-remote --heads origin 'refs/heads/*/ios_*' \
| awk '{print $2}' | sed 's|refs/heads/||'3. worktree 核心操作
查询
git worktree list # 查看所有 worktree
git worktree list --porcelain # 详细信息(机器可读)
git branch --show-current # 当前目录在哪个分支
git status --short # 当前工作区是否干净创建:4 种方式(决策表)
4 种创建方式对应不同场景,缺一不可。别背命令,记场景:
| 场景 | 命令 | 注意 |
|---|---|---|
| 检出已有本地分支 | git worktree add <path> <branch> | 同一分支不能同时检出到两个 worktree |
| 从当前 HEAD 切新分支 | git worktree add -b <new> <path> | 适合临时分析/修复分支 |
| 从远端默认分支切新 | git fetch origin main && git worktree add -b <new> <path> origin/main | origin/main 在 refspec 中,可用 |
| 单分支克隆从远端非默认分支切 | git fetch origin <branch> && git worktree add -b <new> <path> FETCH_HEAD | 必须用 FETCH_HEAD,见第 2 节坑 |
删除
git worktree remove <path> # 正常删除(工作区干净时)
git worktree remove --force <path> # 强制删除(丢弃未提交改动,慎用)
git worktree prune # 清理手工 rm -rf 残留的登记信息关键认知:删 worktree ≠ 删分支。删除 worktree 只删工作目录,分支和已提交的 commit 都还在。清理顺序通常是先删 worktree,确认不需要分支了再删分支:
git worktree remove ~/trunk_file/client-android
git branch -d android/release-t6 # 已合并才能 -d
git branch -D android/release-t6 # 强制删未合并分支
git push origin --delete android/release-t6 # 删远端分支如果你曾经手工 rm -rf 删过 worktree 目录(不建议),Git 可能还残留登记信息,用 git worktree prune 清理。日常优先用 git worktree remove,别 rm -rf。
只读分析与锁定
只想让 AI 工具或同事读代码,不想改当前开发现场,用 detached HEAD:
# 只读分析,不绑定新分支
git worktree add --detach ~/workspaces/readonly-client origin/main
# 后面确实要改,再建分支
cd ~/workspaces/readonly-client
git switch -c bugfix-xworktree 放在外接盘、云盘同步目录、临时目录,担心被 prune 清理,可以锁定:
git worktree lock <path> # 锁定
git worktree unlock <path> # 解锁日常本地目录一般不需要锁定。
4. 多仓库任务 workspace + AI Coding 衔接
多仓库任务 workspace
跨仓库问题排查(客户端 + SDK + 协议仓 + 后台),每个仓库单独开 worktree,集中到一个任务目录:
~/workspaces/bug-20260621/
client/ # 客户端仓 worktree
sdk/ # SDK 仓 worktree
protocol/ # 协议仓 worktree(如需要)
docs/ # 日志、现象、复现步骤、脱敏材料
reports/ # AI 资料包、审查报告、根因候选用 git -C 免 cd,直接在各仓库里执行命令:
mkdir -p ~/workspaces/bug-20260621
# 给每个仓库开独立 worktree,-C 指定仓库路径
git -C ~/repos/client worktree add -b bugfix-x ~/workspaces/bug-20260621/client origin/main
git -C ~/repos/sdk worktree add -b bugfix-x ~/workspaces/bug-20260621/sdk origin/main原则:每个仓库仍然是独立 Git 仓库,不要把多个仓库复制混成一个大目录;docs/ 和 reports/ 放任务材料,不和代码混在一起。
分支策略速查
切分支前先明确基分支,这是前置决策:
| 分支类型 | 基分支 | 说明 |
|---|---|---|
| bugfix 版本分支 | 同平台最新发布分支 | 如 t5 基于 t4 切,修线上问题 |
| feature 分支 | master / trunk(最新) | 新功能从最新主干切 |
拉完分支后,记录基分支和基 commit id(短格式),方便回溯和出问题回滚。
给 AI 隔离现场
这一节是 worktree 在 AI 时代的真正价值。不要让 AI 工具直接读你的主仓库。主仓库有未提交改动、有多个分支切换状态、有你不想让模型碰的实验代码。给它一个干净的 worktree 现场,用完即删。
给 AI 准备上下文的套路:先自己用 git 命令把证据收窄,再喂给模型,而不是让模型全仓猜。优先级是 rg / git grep 精确定位 > IDE 全局搜索 > 让模型猜。
# 1. 确认当前分支和改动范围
git branch --show-current
git status --short
git diff --stat
# 2. 定位关键代码
rg "keyword" # ripgrep 搜内容
git grep "keyword" # 只搜 git 跟踪的文件
rg --files | rg "filename" # 搜文件名
# 3. 确认历史改动原因
git log --oneline -10 -- <file>
git blame <file> | head -20
git show <commit> # 看某次提交改了什么
# 4. 把收窄后的证据整理成 reports/bug-context.md,再喂给 AI不同 AI 工具的接入方式:
- 交互式 agent(Claude Code / CodeBuddy / Cursor):直接打开 workspace 根目录
~/workspaces/bug-20260621/,让 AI 能看到跨仓全貌 +docs/日志 +reports/资料包。每个仓库保持独立分支和提交历史,AI 改完你在对应 worktree 里 review diff。 - 外部工具(aider / OpenRouter):不建议让它读整个多仓 workspace。更稳的方式是本地 agent 先读完整 workspace 生成
reports/bug-context.md,外部工具只--read reports/bug-context.md和少数必要文件。
# aider 只读资料包 + 关键文件,不扫全仓
aider \
--model openrouter/xxx \
--no-auto-commits \
--chat-mode ask \
--read ~/workspaces/bug-20260621/reports/bug-context.md \
--read ~/workspaces/bug-20260621/client/src/key-file.kt核心原则:本地 agent 读完整现场做采证和起草,外部高阶模型只读收窄后的资料包做裁决。这和给模型干净的 evidence pack 而非全仓代码是同一个道理。
5. 提交与排查辅助
commit 多行消息
多个 -m 参数会变成独立段落,段落间自动加空行;引号内直接换行则不加空行:
# 方式一:多个 -m,段落间自动空行
git commit -m "fix(audio): 修复 VoIP 通话偶发杂音" \
-m "- 替换 AEC 路径配置\n- 修正 buffer size 计算"
# 方式二:引号内换行,不自动加空行
git commit -m "fix(audio): 修复 VoIP 通话偶发杂音
- 替换 AEC 路径配置
- 修正 buffer size 计算"建议用 Conventional Commits 风格(feat / fix / docs / refactor / perf / chore),方便后续自动生成 changelog 和按类型筛选。
diff / blame / log 排查
当问题可能和最近改动、分支差异、发布版本有关时,先用 git 证据缩小范围,不要直接问模型"哪里有问题"。先让它知道:当前分支、基准分支、改动文件、关键 diff、相关历史 commit。
git diff <base>..HEAD --stat # 当前分支相对基分支改了哪些文件
git log --oneline <base>..HEAD # 当前分支领先基分支的提交
git blame <file> -L 100,150 # 某段代码最后是谁哪次改的
git show <commit> # 看某次提交的完整 diff
git log -S "funcName" --oneline # 搜某段代码是哪个 commit 引入/删除的6. 速查清单
手工驾驶检查清单
创建 worktree 前:
- 明确任务名和日期(如
bug-20260621) - 明确基分支或基 commit
- 确认不是在错误分支上切出(先
git branch --show-current) - 多仓库任务用统一 workspace 目录
创建后:
git branch --show-current确认分支git status --short确认工作区干净- 记录基分支和基 commit
- 把日志和资料包放到
docs//reports/
删除前:
git status --short确认没有未保存改动- 已 push 或确认不需要保留
- 用
git worktree remove删除(别rm -rf) - 如需要,再删本地/远端分支
常见错误
| 错误 | 原因 | 解法 |
|---|---|---|
| 同一分支不能同时检出到两个 worktree | main 已在主目录检出 | 换分支名,或用 -b new-branch,或 --detach |
invalid reference: origin/xxx | 单分支克隆 fetch 非默认分支不创建远程引用 | 用 FETCH_HEAD 代替 origin/xxx |
| 删了 worktree 分支还在 | 删 worktree ≠ 删分支(这是特性不是 bug) | 先 worktree remove,再 branch -d |
| worktree 目录被手工 rm -rf 后状态异常 | Git 残留登记信息 | git worktree prune 清理 |
| worktree 里误删主仓库 | 主仓库和 worktree 共享 .git | 删前先 git worktree list 确认 |
命令速查表
| 场景 | 命令 |
|---|---|
| 查看所有 worktree | git worktree list |
| 基于已有分支创建 | git worktree add <path> <branch> |
| 创建新分支并开 worktree | git worktree add -b <new> <path> origin/main |
| 单分支克隆基于远端非默认分支 | git fetch origin <branch> && git worktree add -b <new> <path> FETCH_HEAD |
| 只读分析 | git worktree add --detach <path> origin/main |
| 删除 worktree | git worktree remove <path> |
| 强制删除 | git worktree remove --force <path> |
| 清理残留登记 | git worktree prune |
| 锁定 / 解锁 | git worktree lock <path> / unlock |
| 删本地分支 | git branch -d <branch>(-D 强制) |
| 删远端分支 | git push origin --delete <branch> |
| 查远端分支 | git ls-remote --heads origin |
| 免 cd 操作仓库 | git -C <repo-path> <command> |
worktree 的价值不在"多一个目录",在于让并行开发成为默认形态,让 AI 拿到干净现场。主仓库保持稳定,每个任务一个 worktree,用完即删——这是大仓库多分支并行开发的工程化底座。