tmux 多 pane / session 跑多个 Agent
用 tmux 把多个 Agent 会话排在一个终端里,session/window/pane 三层抽象的实操,附常用快捷键速查
用 git worktree 跑多个并行 CLI Agent,避免分支切换打断上下文,含 worktree 管理、Agent 隔离与合并策略
内容摘要
`git worktree` 是 Git 自带的子命令,让一份 `.git` 仓库可以同时检出多个分支到**多个独立的工作目录**。换句话说,原本"切分支必须先 stash、还得当心 build 产物"的痛点,被它彻底拆开了:每个分支住自己的目录,互不打扰,但底层共享同一份 `.git/objects` 和 `.git/refs`,磁盘成本几乎只有一份。
git worktree 是 Git 自带的子命令,让一份 .git 仓库可以同时检出多个分支到多个独立的工作目录。换句话说,原本"切分支必须先 stash、还得当心 build 产物"的痛点,被它彻底拆开了:每个分支住自己的目录,互不打扰,但底层共享同一份 .git/objects 和 .git/refs,磁盘成本几乎只有一份。
这个能力在 IDE 时代用处有限——人就一个,再多 worktree 也只能一只手敲键盘。但到了 CLI Agent 时代,情况完全反过来:你可以同时启动 3-10 个 Claude Code / Codex / Gemini CLI 进程,每个 Agent 在自己的 worktree 里独立改代码、跑测试、调工具,彼此完全不打架。Anthropic 自己工程师反复说他们日常会同时挂 10-15 个并行 Claude 会话;社区的 ceiling 经验值是 3-5 个 worktree 比较舒服,再多人脑就跟不上了。
为什么 worktree 是 CLI Agent 的天作之合?三句话讲清楚:
cwd 当工作区,cd 到不同目录就是不同的世界,天然适配多 worktree。.venv、target/ 这些 build artifact 全是 per-worktree,Agent A 不会把 Agent B 的依赖装挂掉。2026 年 11 月 Claude Code CLI 直接把这个模式做成了内建能力:claude --worktree(简写 -w)启动时自动建一个 worktree、跑在隔离环境里;subagent frontmatter 加 isolation: worktree 就能让每个子 Agent 自动获得独立 worktree,结束时若没改动会自动清理。这不是社区拼装出来的脏 trick,而是被官方收编的一等公民工作流。
| 要素 | 作用 |
|---|---|
共享 .git | 所有 worktree 共用同一份 objects/ 和 refs/,磁盘占用 ≈ 一份;commit 在哪个 worktree 提交完都能在其他 worktree git log 看到 |
| 独立工作目录 | 每个 worktree 有自己的 HEAD、index、node_modules、.venv、build 产物,Agent 在里面随便折腾不会污染其他分支 |
| 每个分支只能 checkout 到一个 worktree | Git 强制约束:同一个分支不允许在两个 worktree 同时 checkout,避免索引冲突;想并行就开新分支或用 --detach |
| per-worktree 配置 | git config extensions.worktreeConfig true 后可以给每个 worktree 设单独的 core.sparseCheckout、user.email 等 |
| 轻量管理命令 | add / list / lock / move / prune / remove / repair 七个子命令覆盖全生命周期 |
每个 Agent 看到的"项目"都是完整的代码 + 完整的 git log + 自己的依赖;提交进 .git 后其他 worktree 立即能看到。对 Agent 而言,根本感知不到自己是"并行多开"的——它只看见一个普通的 Git 仓库,这是 worktree 模型最优雅的地方。
下面以一个典型场景演示完整生命周期:主仓在 ~/code/myapp,要同时让 3 个 Claude Code 实例并行做"加登录功能 / 修个 bug / 重构 API"。
# 推荐把所有 worktree 集中放在主仓同级的 .worktrees/ 目录下
# 这样既不污染主目录,也不会被 .gitignore 误伤
cd ~/code/myapp
mkdir -p ../myapp-worktrees
# 1) 新建一个全新分支并 checkout 到 worktree
git worktree add -b feature-login ../myapp-worktrees/feature-login
# 2) 基于已有远端分支创建(追踪 origin/fix-bug-2031)
git worktree add ../myapp-worktrees/fix-bug-2031 origin/fix-bug-2031
# 3) detach 模式(只想看一眼某个 commit,不开分支)
git worktree add --detach ../myapp-worktrees/inspect-v1.2 v1.2.0
git worktree list 验证:
$ git worktree list
/Users/me/code/myapp a1b2c3d [main]
/Users/me/code/myapp-worktrees/feature-login e4f5g6h [feature-login]
/Users/me/code/myapp-worktrees/fix-bug-2031 h7i8j9k [fix-bug-2031]
/Users/me/code/myapp-worktrees/inspect-v1.2 k1l2m3n (detached HEAD)
最朴素的写法是开三个终端窗口,每个 cd 到对应 worktree 后跑 claude。但更工程化的做法是用 tmux / zellij 自动化:
# 用 tmux 一次开 3 个 pane,每个 pane 跑一个独立的 Claude Code 会话
tmux new-session -d -s agents -n login \
"cd ~/code/myapp-worktrees/feature-login && claude"
tmux new-window -t agents:1 -n bugfix \
"cd ~/code/myapp-worktrees/fix-bug-2031 && claude"
tmux new-window -t agents:2 -n refactor \
"cd ~/code/myapp-worktrees/refactor-api && claude"
tmux attach -t agents
# Ctrl-b 1 / 2 / 3 在三个 Agent 之间切换
如果用 Claude Code 自己内建的 worktree 模式(CLI 0.x 起支持),可以直接:
# 一行启动:自动建 worktree + 自动起会话
claude --worktree feature-login
# 等价于在 .claude/worktrees/feature-login/ 起一个隔离的 Claude 会话
# 加 --tmux 还会顺便丢进 tmux 会话
claude --worktree fix-bug-2031 --tmux
如果用 subagent 派活,把 worktree 隔离写进 frontmatter 即可:
---
name: parallel-refactor
description: 并行重构多个模块
isolation: worktree # ← 关键
---
你的任务是同时重构 auth、billing、notifications 三个模块……
主 Agent 派出去的每个子 Agent 自动获得独立 worktree,结束时没改动的会自动清理,有改动的会保留供你 review。
每个 worktree 就是一个普通的 git 工作目录,提交、push、开 PR 都和单仓一模一样:
cd ~/code/myapp-worktrees/feature-login
git add .
git commit -m "feat: add login form"
git push -u origin feature-login
gh pr create --fill
如果走"小步快跑直接合 main"的本地化流程,可以写一个收尾脚本:
#!/usr/bin/env bash
# scripts/wt-merge.sh —— 把指定 worktree 的分支 merge 回 main 并清理
set -euo pipefail
BRANCH=$1
ROOT=~/code/myapp
WT=~/code/myapp-worktrees/$BRANCH
# 1) worktree 内必须 clean
cd "$WT"
if [[ -n "$(git status --porcelain)" ]]; then
echo "❌ $BRANCH 还有未提交修改,请先处理"; exit 1
fi
# 2) 主仓拉一下,merge --no-ff 保留分支语义
cd "$ROOT"
git fetch --all --prune
git checkout main && git pull
git merge --no-ff "$BRANCH" -m "merge: $BRANCH"
git push origin main
# 3) 清理 worktree 与本地分支
git worktree remove "$WT"
git branch -d "$BRANCH"
echo "✅ $BRANCH 已合入 main 并清理"
跑一次:./scripts/wt-merge.sh feature-login。
# 正常移除(要求 clean)
git worktree remove ../myapp-worktrees/feature-login
# 强制移除(worktree 内还有未提交修改时)
git worktree remove -f ../myapp-worktrees/inspect-v1.2
# 手动 rm -rf 后用 prune 清理孤立元数据
rm -rf ../myapp-worktrees/old-experiment
git worktree prune -v
# 看一眼有没有要 prune 的(dry-run)
git worktree prune -n
预期输出:
$ git worktree list
/Users/me/code/myapp a1b2c3d [main]
/Users/me/code/myapp-worktrees/refactor-api x1y2z3w [refactor-api]
孤立 worktree 不主动清理会一直占着 .git/worktrees/<name>/ 元数据,定期 git worktree prune 是个好习惯。
| 维度 | git worktree | git stash + 切分支 | 多次 git clone | 容器/Devcontainer 隔离 |
|---|---|---|---|---|
| 磁盘占用 | 共享 .git,每个 worktree 只多一份源码 + 依赖 | 极省(单目录) | 每个 clone 都是完整 .git,几倍体积 | 镜像层共享但容器可能 GB 级 |
| 切换成本 | cd 即切,秒级 | stash → checkout → stash pop,依赖重装 | cd 即切但 fetch 重复 | docker exec / devcontainer attach,秒-十秒级 |
| 并行 Agent 能力 | ✅ 天然支持,每个 worktree 独立 cwd | ❌ 单工作区,多 Agent 必撞 | ✅ 但 commit 不共享,需要 push/fetch 同步 | ✅ 强隔离,但启动重 |
| 共享 commit 历史 | ✅ 同一个 .git,commit 立即可见 | ✅ | ❌ 必须 fetch | 取决于挂载方式 |
| 上手成本 | 低(7 个子命令) | 极低 | 极低 | 中-高(要写 Dockerfile) |
| 依赖隔离 | per-worktree(node_modules 各装各的) | ❌ 同一份 node_modules | ✅ | ✅ 镜像级 |
| 适用场景 | 多 Agent 并行 / 多分支并行评审 | 临时切换、单 Agent 工作流 | 跨机器或需要完全独立历史 | 强 sandbox / 危险权限 / 需要独立 OS 环境 |
核心区别一句话:stash + 切分支是给"一个人"用的快捷键;worktree是给"多个人或多个 Agent"用的真并行;多次 clone是把"多个仓库"当"一份代码"维护,commit 同步成本高;容器隔离提供的是 OS 级别的安全沙箱,量级和 worktree 不在一个层面。对 CLI Agent 并行场景,worktree 几乎是唯一兼顾"轻量 + commit 共享 + 真隔离"的答案。
| 误区 | 准确理解 |
|---|---|
以为 worktree 共享 .git 就会出现 commit 冲突 | 共享的是 objects/、refs/heads/、refs/remotes/ 这些只追加的存储;每个 worktree 的 HEAD、index、refs/bisect、refs/worktree/ 是独立的,commit 之间不会互相覆盖 |
把 worktree 创建到 .git/ 内部,比如 .git/worktrees/feature | .git/worktrees/<name>/ 是 Git 自己管理的元数据目录,不要把工作目录塞进去;标准做法是放在主仓同级的 ../<repo>-worktrees/<branch>/ 或 ~/code/myapp-worktrees/ |
| 同一个分支想在两个 worktree 同时 checkout | Git 直接报错:"is already checked out";要么 git worktree add --force 覆盖(不推荐),要么开一个新分支,要么用 --detach 看个只读快照 |
| 以为 worktree 之间 node_modules / .venv / .env 自动共享 | 完全不共享。每个 worktree 都要单独 npm install / pip install / 复制 .env。pnpm 的 content-addressable store 可以缓解依赖磁盘成本;.env 这类敏感文件可以用一个根目录脚本批量 symlink |
删 worktree 直接 rm -rf 完事 | 文件删了但 .git/worktrees/<name>/ 元数据还在,长期会变成"幽灵 worktree",污染 git worktree list;正确做法是 git worktree remove,或者删完后跑一次 git worktree prune |
给每个 Agent 都让它自己 git worktree add,然后忘了管 | Agent 不会自己清理。要么用 Claude Code --worktree / isolation: worktree 让框架自动收尾,要么写一个晚上跑的 cron:git worktree list --porcelain 找出 30 天没动的 prune 掉 |
| 以为 worktree 一定要在本机 SSD 上 | worktree 完全可以放在 NAS、外置盘、USB 上,配合 git worktree lock --reason "on USB drive" 可以避免在设备未挂载时被误 prune |
| 优势 | 劣势 |
|---|---|
| 真并行:N 个 Agent 同时跑 N 个分支,互不污染 index / build 产物 / 测试运行时 | 依赖要装 N 份:node_modules / .venv / target 是 per-worktree,磁盘和首次 install 时间 ×N |
| 共享 commit 历史:在 worktree A 里看 B 刚 push 的 commit,零延迟 | .env 等本地配置要手动同步:Git 不管 gitignored 文件,新建 worktree 不会自动复制 .env / .local.json 等 |
轻量:.git 共享,相比 N 份 clone 节省大量磁盘 | 同分支不能并行 checkout:Git 强制约束,必须为每个并行任务开独立分支 |
官方一等公民:Git 自带(2015 起),Claude Code 0.x(2026)/ 多家工具内建 --worktree 模式 | 管理复杂度上升:worktree 多了之后忘记 prune、忘记 merge 的"烂尾分支"会越积越多 |
| 天然适配 Agent 工作流:subagent 派活时每个子 Agent 一个 worktree,互不干扰 | 跨 worktree 调试困难:单元测试、IDE 跳转都局限在自己的工作目录里,跨 worktree 的代码搜索不能直接 grep |
可锁定:git worktree lock 让外置设备/远程挂载安全 | 新人心智负担:未接触过 worktree 的开发者第一次看到 .git 是个文件而不是目录会困惑 |
参考答案:
git stash + git switch 本质是单工作目录的状态切换——把当前修改藏起来,腾空目录给另一个分支。它解决的是"我手头工作没做完,但临时要去看另一个分支"。代价是:每次切换都要 stash / pop,build artifact 和 node_modules 是同一份,切到另一个分支可能因为依赖版本不同而需要重装。
git worktree 是多工作目录并存——同一个仓库同时有多个 checkout,每个住自己的目录、有自己的依赖。代价是磁盘要多占几份源码、依赖也要多装几份。
判断"值不值得用 worktree"看一条线:你需不需要让两份工作同时存在、同时跑?
CLI Agent 时代基本是后两种,所以 worktree 才突然成了高频工具。
参考答案:
不会受影响。每个 worktree 的工作目录是物理独立的,.env 也是各占一份,A 改不到 B 的文件。worktree 共享的只是 .git 里的 objects/ 和 refs/,而 .env 在 .gitignore 里、根本不进 Git,所以谈不上跨 worktree 同步。
但这也带来副作用:新建 worktree 时 .env 不会自动从主仓复制过去——Agent 跑起来发现少了环境变量,第一反应是它自己去填一个错的。
设计上有几种推荐做法:
ln -s ../../myapp/.env .env,所有 worktree 指向同一份 .env。改一处全部生效。坏处是如果某个 Agent 在实验性改 .env,会污染其他 Agent。scripts/wt-sync-env.sh,新建 worktree 后从主仓拷一份 .env 过去。每个 worktree 独立,互不影响。需要时手动同步。.worktreeinclude 法(社区方案):在仓库根放一个 .worktreeinclude,列出要复制到新 worktree 的 gitignored 路径;配合工具如 workmux、agent-cli dev 自动处理。cp .env .env.experiment,让 Agent 用 --env-file .env.experiment 启动,不污染原 .env。最佳实践通常是 2 + 4 组合:默认每个 worktree 独立一份 .env,需要全局调整时统一脚本下发;做实验用专门的 .env.experiment 隔离。
参考答案:
核心思路是约束创建、监控存活、自动清理三段式。
1. 约束创建(防止 worktree 无序膨胀)
写一个团队共用的 wt-new 脚本:
#!/usr/bin/env bash
# 限制最多 5 个并行 worktree
COUNT=$(git worktree list --porcelain | grep -c '^worktree ')
if [[ $COUNT -ge 6 ]]; then # +1 是主 worktree
echo "❌ 已有 5 个并行 worktree,先清理再开新的"
git worktree list
exit 1
fi
git worktree add -b "$1" "../$(basename $PWD)-worktrees/$1"
# 顺手把 .env 同步过去
cp .env "../$(basename $PWD)-worktrees/$1/.env" 2>/dev/null || true
2. 监控存活(标记 stale worktree)
用 git worktree list --porcelain 解析路径,结合 git -C <wt> log -1 --format=%ct 拿最后一次 commit 时间戳,超过 3 天没动的写入告警:
# scripts/wt-stale-check.sh
NOW=$(date +%s)
git worktree list --porcelain | awk '/^worktree /{print $2}' | while read -r WT; do
LAST=$(git -C "$WT" log -1 --format=%ct 2>/dev/null || echo 0)
AGE=$(( (NOW - LAST) / 86400 ))
if (( AGE > 3 )); then
echo "⚠️ $WT 已 $AGE 天没活动"
fi
done
cron 一行:0 9 * * * cd ~/code/myapp && ./scripts/wt-stale-check.sh | mail -s "stale worktrees" me@team。
3. PR 合并后自动 prune(闭环)
GitHub Actions 触发本地 webhook,或更简单:每天夜里跑一个清理脚本,拿 gh pr list --state merged 的分支名和 git worktree list 求交集,删掉那些"分支已合并、worktree 还在"的:
# scripts/wt-cleanup-merged.sh
gh pr list --state merged --json headRefName -q '.[].headRefName' | while read -r BRANCH; do
WT=$(git worktree list --porcelain | awk -v b="$BRANCH" '
/^worktree /{path=$2} /^branch /{if($2=="refs/heads/"b) print path}')
[[ -n "$WT" ]] && git worktree remove "$WT" && git branch -d "$BRANCH"
done
git worktree prune -v
额外约束:在 .git/hooks/post-merge 里加一行 git worktree prune,每次 merge 完顺手清理孤立元数据;团队 README 写明"实验性 worktree 命名以 wip/ 开头",方便定期批量清理。
这套组合就能把"5 个 worktree 上限 + 3 天 stale 告警 + 合并自动清理"做成一个不依赖人工记忆的治理闭环。
--worktree、subagent isolation):https://code.claude.com/docs/en/common-workflows优先展示同分类且标签更接近的内容,方便继续串联学习。
用 tmux 把多个 Agent 会话排在一个终端里,session/window/pane 三层抽象的实操,附常用快捷键速查
Obsidian v1.12.4 起官方 CLI 全量上线,让 Agent 通过 Bash 直接调 obsidian 命令毫秒级读写 vault,附 Agent 提示词模板与对比 MCP 路径的取舍
在 CLI Agent 里接入第三方工具的决策框架 —— 何时直接 Bash 调外部 CLI,何时走 MCP server,含主流工具速查表