并行开发利器:worktree

0 阅读8分钟

从一个熟悉的场景说起

你正处于心流状态,代码写得行云流水,feature 分支上的新功能即将成型。这时候,Slack 弹出一条消息——线上出了 P0 故障,需要立刻修复。又或者,产品经理冲过来说"有个紧急需求,老板今天要看"。

怎么办?git stash 把当前改动藏起来,切到 main 拉个分支修 bug,修完再切回来 git stash pop?这套操作你可能已经做过无数次了。但 stash 栈一旦积攒多了,哪个是哪个根本分不清。更别提 stash 之前还得确认哪些文件该暂存、哪些不该——稍有不慎,改动就丢了。

再看看 AI 编程的场景。你想让两个任务同时推进:一个做功能开发,一个做代码重构。但两个任务如果在同一个工作目录里交替进行,文件改动互相覆盖,结果只会一团乱麻。什么,复制一份代码仓库?几个 G 的 .git 目录再来一份,硬盘表示抗议。

其实,Git 早就替你想好了解决方案:worktree

Git Worktree:一个仓库,多个工作目录

git worktree 允许你在同一个仓库下同时检出多个工作目录,每个目录对应不同的分支。无需 stash,无需 commit 半成品代码,就能并行处理多个任务。

核心概念

  • 每个 git 仓库有一个主工作树(main worktree),就是你 clone 时创建的那个目录
  • 通过 git worktree add 可以创建链接工作树(linked worktree),它们共享同一个 .git 仓库数据
  • 每个工作树必须检出不同的分支——同一分支不能同时被两个工作树使用

一句话概括:多个目录,一份仓库数据,各干各的活。

下面这张架构图展示了 Git Worktree 的运行原理——一个共享的 .git 仓库,挂载多个独立的工作树,每个工作树可以从不同的基准分支创建:

flowchart TB
    subgraph repo["共享 .git 仓库"]
        objects[("对象库\ncommits / trees / blobs")]
        refs["引用\nbranches / tags"]
        meta["config / hooks\nstash / reflog"]
    end

    repo <-->|"读写"| main_wt
    repo <-->|"读写"| hotfix_wt
    repo <-->|"读写"| feature_wt

    subgraph main_wt["主工作树  ~/project/"]
        m1["HEAD -> main"]
        m2["独立 INDEX"]
        m3["工作文件"]
    end

    subgraph hotfix_wt["链接工作树  ../hotfix/"]
        h1["HEAD -> hotfix/login-crash"]
        h2["独立 INDEX"]
        h3["工作文件"]
    end

    subgraph feature_wt["链接工作树  ../feature/"]
        f1["HEAD -> feature/new-auth"]
        f2["独立 INDEX"]
        f3["工作文件"]
    end

    base_main(["main 分支"]) -.->|"基准分支"| hotfix_wt
    base_dev(["origin/dev 分支"]) -.->|"基准分支"| feature_wt

    style repo fill:#e8f4fd,stroke:#2196F3,color:#000
    style main_wt fill:#e8f5e9,stroke:#4CAF50,color:#000
    style hotfix_wt fill:#fff3e0,stroke:#FF9800,color:#000
    style feature_wt fill:#fce4ec,stroke:#E91E63,color:#000
    style base_main fill:#fff,stroke:#999,stroke-dasharray: 5 5,color:#000
    style base_dev fill:#fff,stroke:#999,stroke-dasharray: 5 5,color:#000

常用命令

1. 创建工作树

# 基于现有分支创建工作树
git worktree add ../hotfix-dir hotfix/critical-bug

# 创建新分支并检出到工作树
git worktree add -b feature/new-login ../new-login-dir

# 基于远程分支创建
git worktree add ../review-dir origin/feature/review

2. 查看所有工作树

git worktree list

输出示例:

/home/user/my-project          abc1234 [main]
/home/user/hotfix-dir          def5678 [hotfix/critical-bug]
/home/user/new-login-dir       789abcd [feature/new-login]

3. 删除工作树

# 推荐:直接 remove【推荐】
git worktree remove ../hotfix-dir

# 或者手动删除目录后清理记录
rm -rf ../hotfix-dir
git worktree prune

典型使用场景

场景一:紧急修复 Bug,不打断当前开发

你正在 feature/dashboard 上写代码,线上突然炸了:

# 不需要 stash,直接创建新工作树
git worktree add -b hotfix/login-crash ../hotfix main

cd ../hotfix
# 修复 bug、提交、推送
vim src/login.js
git commit -am "fix: login crash on null user"
git push origin hotfix/login-crash

# 修完后回到原工作目录继续开发
cd ../my-project
git worktree remove ../hotfix

全程没有碰过原来的工作目录,feature/dashboard 上写到一半的代码纹丝不动。

场景二:同时运行两个分支做对比

# 主目录运行 main 分支的服务
cd ~/project && npm run dev  # 端口 3000

# 另一个工作树运行 feature 分支
git worktree add ../project-feature feature/new-api
cd ../project-feature && PORT=3001 npm run dev  # 端口 3001

# 浏览器同时打开两个端口,直接对比效果

场景三:Code Review 时查看 PR 代码

# 拉取 PR 分支到独立工作树
git fetch origin pull/42/head:pr-42
git worktree add ../pr-42-review pr-42

cd ../pr-42-review
# 阅读代码、运行测试,不影响主工作区
npm test

# 审完清理
cd ../my-project
git worktree remove ../pr-42-review

Worktree vs 其他方案

方案切换成本磁盘占用并行工作
git stash + checkout高(需保存/恢复状态)不支持
git clone(多份副本)高(完整仓库副本)支持
git worktree低(共享 .git支持

注意事项

  • 同一分支不能同时被两个工作树检出
  • 不要直接 rm -rf 工作树目录了事,应使用 git worktree remove 或删后执行 git worktree prune
  • 工作树之间共享 stash、reflog 等仓库级数据
  • 子模块(submodule)在工作树中需要额外初始化

Claude Code 内置 Worktree 支持

Claude Code 的负责人 Boris Cherny 在 X 上宣布:Claude Code CLI 现在内置了 Git Worktree 支持,各智能体可以并行运行,互不干扰。

image.png

这意味着 AI 辅助编程进入了"多线程"时代——你可以同时启动多个 Claude 会话,每个会话在独立的 worktree 中工作,彼此完全隔离。

image.png 下图展示了 Claude Code Worktree 的整体架构——开发者通过 CLI 启动多个 Agent 会话,每个会话在 .claude/worktrees/ 下的独立工作树中运行,共享同一个 .git 仓库,同时支持通过 Hooks 扩展到非 Git 版本控制系统:

flowchart TB
    user(["开发者"])

    user -->|"终端1: claude -w feature-auth"| agent1["Claude Agent\n会话 1"]
    user -->|"终端2: claude -w bugfix-123"| agent2["Claude Agent\n会话 2"]
    user -->|"终端3: claude -w refactor"| agent3["Claude Agent\n会话 3"]

    agent1 -->|"读写文件 / 执行命令"| wt1
    agent2 -->|"读写文件 / 执行命令"| wt2
    agent3 -->|"读写文件 / 执行命令"| wt3

    subgraph claude_dir[".claude/worktrees/"]
        wt1["feature-auth/\n分支: feature-auth\n基于 HEAD"]
        wt2["bugfix-123/\n分支: bugfix-123\n基于 HEAD"]
        wt3["refactor/\n分支: refactor\n基于 HEAD"]
    end

    wt1 & wt2 & wt3 -->|"共享"| git[(".git 仓库")]

    subgraph hooks_ext["Hooks 扩展(非 Git 项目)"]
        hooks["WorktreeCreate\nWorktreeRemove"]
        hooks -->|"适配"| hg["Mercurial"]
        hooks -->|"适配"| p4["Perforce"]
        hooks -->|"适配"| svn["SVN"]
    end

    git ~~~ hooks_ext

    style user fill:#f5f5f5,stroke:#333,color:#000
    style claude_dir fill:#e8f4fd,stroke:#2196F3,color:#000
    style git fill:#e8f5e9,stroke:#4CAF50,color:#000
    style hooks_ext fill:#fff3e0,stroke:#FF9800,color:#000
    style agent1 fill:#ede7f6,stroke:#673AB7,color:#000
    style agent2 fill:#ede7f6,stroke:#673AB7,color:#000
    style agent3 fill:#ede7f6,stroke:#673AB7,color:#000

Claude Code Worktree 用法

启动方式

# 指定名称创建 worktree
claude --worktree feature-auth
claude -w bugfix-123

# 自动生成名称
claude -w

执行后,Claude Code 会:

  1. .claude/worktrees/<名称>/ 下创建独立的工作目录
  2. 基于当前 HEAD 自动创建一个同名的 git 分支
  3. 在该隔离环境中启动 Claude 会话

典型工作流

# 终端 1:开发新功能
claude --worktree feature-new-auth

# 终端 2:修复 bug(同一仓库,独立环境)
claude --worktree bugfix-dashboard

# 终端 3:重构代码
claude --worktree refactor-utils

三个会话各自独立运行,互不影响。

在会话内创建 worktree

在 Claude Code 交互过程中,也可以随时创建 worktree 来隔离工作:

> 请在 worktree 中工作

Claude 会自动调用内置的 worktree 功能,创建隔离环境并切换过去。

通过 Hooks 扩展到非 Git 项目

Claude Code 的 worktree 功能并不局限于 Git 仓库。通过配置 WorktreeCreateWorktreeRemove hooks,可以将这套隔离机制扩展到 Mercurial、Perforce、SVN 等版本控制系统。对于使用非 Git 版本控制的团队,这提供了一种统一的并行开发方式。

image.png

清理

使用完毕后,退出会话时 Claude Code 会提示你是否保留或清理 worktree。建议在 .gitignore 中添加:

.claude/worktrees/

Git Worktree vs Claude Code Worktree

两种 worktree 用法都能实现并行开发,但在细节上有明显差异:

对比维度Git WorktreeClaude Code Worktree
依赖仅依赖 Git依赖 Claude Code CLI
基准分支可从任意分支/commit 创建只能从当前 HEAD 创建
工作树位置自由指定路径固定在 .claude/worktrees/
分支命名完全自定义与 worktree 名称绑定
适用场景通用 Git 工作流Claude Code AI 编程场景
非 Git 支持不支持通过 Hooks 可扩展
会话管理支持 /resume 切换会话

推荐优先使用 git worktree 理由很简单:

第一,它不依赖任何第三方工具。不管你用 VS Code、Vim、JetBrains 还是 Claude Code,git worktree 都能正常工作。工具链的依赖越少,工作流就越稳定。

第二,git worktree 支持从任意分支或 commit 创建工作树,而 Claude Code 只能基于当前 HEAD。当你需要从 main 分支拉一个 hotfix,但当前在 feature/xyz 上时,git worktree add -b hotfix ../hotfix main 一条命令搞定,Claude Code 的 worktree 做不到这一点。

当然,如果你的工作流完全围绕 Claude Code 展开,或者需要用到非 Git 版本控制系统的 Hooks 扩展能力,Claude Code 的 worktree 仍然是一个不错的选择。

Worktree 存在的问题

Worktree 并非银弹,使用中有两个绕不开的问题。

依赖和缓存需要重新加载

每个 worktree 是一个独立的工作目录,但 node_modules、构建缓存、IDE 索引等并不会共享。新建一个 worktree 后,你通常需要重新 npm install(或 pnpm install),等待依赖安装完成,IDE 也需要重新索引。对于大型项目,这个开销并不小。

一些缓解办法:使用 pnpm 的全局 store 可以减少重复下载;把构建缓存放到仓库外的共享目录;或者在创建 worktree 后用脚本自动初始化环境。但无论如何,这笔"启动成本"是客观存在的。

代码冲突被推迟到 PR 阶段

多个 worktree 并行开发时,每个人在自己的分支上写得很顺畅,冲突感知为零。但这不代表冲突不存在——它只是被推迟了。等到多个分支的 PR 同时合入主干时,冲突会集中爆发。

特别是当多个 worktree 修改了同一个文件的相邻区域时,合并冲突往往比预期的更复杂。建议在并行开发时保持对主干的频繁 rebase,尽早暴露冲突,而不是等到最后才面对一堆 conflict markers。

结语

无论是传统的多分支开发,还是 AI 驱动的多智能体协作,并行开发正在成为常态。git worktree 作为 Git 原生提供的轻量级方案,用最小的磁盘开销和最低的切换成本,解决了"一个仓库、多条战线"的问题。

掌握 worktree,是每个现代开发者的必备技能。下次再遇到"正在写代码突然要切分支"的场景,不要再 git stash 了——开个 worktree,两边的活一起干。