ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

qwen-code Worktree 通用能力:从设计文档到源码实现的隔离工作环境全解

qwen-code Worktree 通用能力:从设计文档到源码实现的隔离工作环境全解 qwen-code Worktree 通用能力从设计文档到源码实现的隔离工作环境全解【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-codeqwen-code 把 git worktree 从 Arena 多模型对比的专属机制升级为面向普通用户会话与 subagent 的通用能力。本文基于设计文档 worktree 通用能力设计结合仓库中已落地的工具、服务与 CLI 启动路径源码完整讲解enter_worktree/exit_worktree两个工具、AgentTool的isolation: worktree参数、--worktree启动标志与 PR 引用解析的实现原理、配置项与安全守卫。读完后你将掌握 qwen-code 中 worktree 的目录约定、会话持久化机制、脏状态清理策略以及如何在会话内、subagent 中或启动时开启隔离工作环境。一、背景从 Arena 专属到通用能力qwen-code 此前只有面向 Arena 多模型对比场景的内部 worktree 实现GitWorktreeService普通用户无法在会话中使用 worktree 隔离工作AgentTool 也不支持为 subagent 创建隔离环境。设计文档给出的目标是将 worktree 做成通用能力支持用户会话级隔离和 Agent 级隔离同时保证现有 Arena 功能体验完全不变。现状能力对比来自设计文档如下可以看到 qwen-code 在 Phase AD 各阶段已补齐的能力与 claude-code 的对照功能qwen-codeclaude-code阶段EnterWorktree工具已有Phase A已有—ExitWorktree工具已有Phase A已有—AgentToolisolation: worktree已有Phase B已有—过期 worktree 自动清理已有Phase B已有—worktree 会话状态持久化与恢复已实现Phase C已有Phase CPost-creation setuphooks 配置已实现Phase C已有Phase CStatusLine worktree 状态展示已实现Phase C已有Phase CWorktreeExitDialog退出提示已实现Phase C已有Phase C--worktreeCLI 启动标志已有Phase D已有—符号链接目录node_modules 等已有Phase D已有—PR 引用--worktree#123已有Phase D已有—sparse checkout未实现已有Futuretmux 集成未实现已有FutureArena 多模型 worktree 隔离qwen 独有无—脏状态覆盖stash copy已有已有—Baseline commit 追踪qwen 独有无—其中「脏状态覆盖」与「Baseline commit 追踪」两项 qwen-code 独有的能力在 gitWorktreeService.ts 中可以看到实现痕迹setupWorktrees()L1016在批量创建 Arena worktree 时用git stash create捕获已跟踪的脏状态、对未跟踪文件做逐文件 copy并写入一个baseline (dirty state overlay)的 baseline commitBASELINE_COMMIT_MESSAGEL559后续 diff 优先相对该 baseline 计算L1357 起没有 baseline 时回退到 merge-base。二、设计原则通用层与 Arena 层解耦设计文档明确了核心原则worktree 是通用能力Arena 是其上层应用。通用 worktree 层EnterWorktree/ExitWorktree工具、AgentToolisolation参数、会话状态管理、自动清理Arena 层多模型并行调度、worktreeBaseDir自定义路径、批量创建与 diff 对比继续使用GitWorktreeService.setupWorktrees()的现有逻辑不受通用层改动影响。两条路径在架构上是独立的AgentTool 的isolation: worktree只走通用路径Arena 内部不经过这个参数创建 worktree。这一点在配置 schema 中也有明确声明——settingsSchema.ts 中worktree顶层项的 description 写明该配置只作用于enter_worktree工具、agent isolation: worktree参数与启动--worktree标志创建的 worktree不影响Arena worktreeArena 用agents.arena.worktreeBaseDir默认~/.qwen/arena。三、路径约定与命名规则通用 worktree 路径由EnterWorktree工具或 AgentToolisolation: worktree创建的 worktree 固定存放在{git 仓库根}/.qwen/worktrees/{slug}路径不可配置。slug 命名规则用户会话 worktree用户指定名称或自动生成格式{形容词}-{名词}-{4位随机}。工具实现中的注释enter-worktree.ts写为{adj}-{noun}-{4hex}Agent worktreeagent-{7位随机 hex}对应源码中的AGENT_WORKTREE_SLUG_PATTERNgitWorktreeService.ts。分支命名沿用worktree-slug规则由worktreeBranchForSlug()生成。由于 worktree 固定在仓库根下的.qwen/worktrees/enter-worktree.ts 在执行前会先用一个「probe」服务解析 git 仓库顶层确保即使从 monorepo 子目录调用工具worktree 也落在repoRoot/.qwen/worktrees/下而不是散落在各个包目录里。Arena worktree 路径Arena 的 worktree 路径由agents.arena.worktreeBaseDir控制默认~/.qwen/arena与通用路径完全独立通用层不做任何改动。四、Phase AEnterWorktree 工具触发条件与输入 Schema设计文档规定的触发条件在工具描述中得到了忠实落实enter-worktree.ts只有用户明确说 start a worktree、use a worktree、create a worktree 等词语时才调用用户说修复 bug、开发功能、创建分支时不得触发。输入 schemaname?: string // 可选slug 格式字母/数字/点/下划线/破折号最大 64 字符工具注册在 tool-names.tsENTER_WORKTREE: enter_worktree、EXIT_WORKTREE: exit_worktree并设置了shouldDefer标志——只有用户明确提及 worktree 时才会被模型调用见 enter-worktree.ts 构造函数参数。执行流程源码级设计文档描述的行为序列与 EnterWorktreeInvocation.execute() 的实现一一对应防嵌套校验若当前targetDir路径中含.qwen/worktrees/组件直接拒绝Already inside a git worktree...。源码注释解释了原因嵌套创建会让模型的上下文仍指向外层 worktree退出时内层 worktree 通常被孤立git 可用性检查与仓库根解析checkGitAvailable()isGitRepository()随后用getRepoTopLevel()解析到仓库顶层slug 处理空字符串按未提供处理部分模型会对可选参数传未提供时调用GitWorktreeService.generateAutoSlug()L1596显式 slug 经validateUserWorktreeSlug()L2029校验该函数还会保留agent-前缀防止用户命名意外撞上 Agent 临时 worktree 的清理模式锚定基础分支getCurrentBranch()捕获调用时的检出分支作为 base源码注释指出若省略 base会回退到主工作区当前分支而用户可能正站在 feature 分支上启动同时用getCurrentCommitHash()记录originalHeadCommit供退出对话框统计本次会话新增提交创建 worktree调用service.createUserWorktree(slug, baseBranch, { symlinkDirectories })symlinkDirectories直接取自config.getWorktreeSymlinkDirectories()Phase D-2 配置见下文写会话标记writeWorktreeSessionMarker(worktreePath, sessionId)把 worktree 打上所属会话的标签best-effort失败不中止创建使后续跨会话的exit_worktree actionremove能拒绝删除别人的工作持久化 sidecarwriteWorktreeSession()写入WorktreeSession记录包含{ slug, worktreePath, worktreeBranch, originalCwd, originalBranch, originalHeadCommit }设计文档中为{ slug, worktreePath, worktreeBranch, originalCwd, originalBranch }实现中额外增加了originalHeadCommit字段服务于--resume恢复、Footer 展示与退出对话框输出worktreePath、worktreeBranch、message。message 明确指示模型「从此刻起所有文件操作都走该绝对路径直到调用exit_worktree」。设计文档中提到 Phase A 的EnterWorktreeTool不修改Config.targetDir依赖模型从工具结果里读到绝对路径并继续使用——源码印证了这一点execute 全程只读config.getTargetDir()没有调用setTargetDir。五、Phase AExitWorktree 工具与安全守卫输入 Schema 与触发条件name: string // 必须与 enter_worktree 使用/返回的 name 一致 action: keep | remove discard_changes?: boolean // 仅 actionremove 时有效触发条件用户说 exit the worktree、leave the worktree、were done with the worktree 等。三层安全守卫ExitWorktreeInvocation.execute() 实现了设计文档要求的守卫并比文档描述更严格会话所有权守卫remove前读取 worktree 的 session markerreadWorktreeSessionMarker若 owner 是其他活跃会话则拒绝执行防止提示注入或混乱模型枚举.qwen/worktrees/后误删他人工作owner 未知无 marker时放行但记 warn 日志未提交变更守卫countWorktreeChanges()统计 tracked/untracked 变更discard_changes: false时只要有变更就拒绝若git status本身失败counts 为 null宁可拒绝也不建议 bypass——因为此时安全检查的前提条件未知未合并提交守卫hasUnmergedWorktreeCommits()检测分支上是否有其他分支或远程 ref 不可达的提交。设计文档只提到未提交变更守卫源码还额外拒绝了「删除分支会丢提交」的场景且没有提供 discard commits 的开关——注释解释了理由「丢失已提交的工作很少是用户说 remove worktree 时真正的意思」。此外权限层面actionremove的getDefaultPermission()返回ask并覆写getConfirmationDetails()返回type: exec而非默认info源码注释说明了动机——AUTO_EDIT模式会自动批准info/edit类型确认必须让删除 worktree 走run_shell_command同级的确认通道才能防住数据丢失路径。keep / remove 行为keep保留 worktree 目录和分支模型继续引用绝对路径。注意实现与文档的一处演进设计文档写的是「清空会话中的 worktree 状态」而当前源码在keep时保留sidecarexit-worktree.ts 注释引用了 PR 评审意见理由是清空绑定会让后续--resume/ Footer / 退出对话框「忘记」用户刚选择保留的 worktreeremove删除 worktree 目录与分支随后maybeClearWorktreeSession()仅当 sidecar 中的 slug 与本次退出的 slug 一致时才清除用户可能在磁盘上有多个 worktreesidecar 只跟踪一个避免误伤。若git branch -d在安全检查通过后又拒绝删除并发写入竞态工具会保留分支并在 message 中告知git branch -D手动恢复而不是强删。六、Phase BAgent 级隔离与过期清理isolation: worktree 参数设计模型可为 subagent 创建临时隔离 worktreeagent 结束后自动清理。在 agent.ts 中isolation?: worktree参数的说明为创建临时 worktree 于projectRoot/.qwen/worktrees/agent-7hex无变更则自动删除有变更则保留将路径和分支返回在结果中——与文档完全一致。工具描述L901补充无变更自动清理有变更时结果中返回 worktree 路径与分支供审查或合并。校验规则agent.tsisolation只接受worktree要求显式subagent_type不能是 forkfork 共享父级上下文不需要隔离命名 teammate 不允许isolation——需要先创建 leader 拥有的 worktree再通过working_dir参数 pin 进去对应 worktree-pin.ts 的实现。buildWorktreeNotice隔离环境上下文注入设计文档要求参考 claude-code 的buildWorktreeNoticefork subagent 在 worktree 中运行时向其注入上下文提示——说明其处于隔离 worktree、路径继承自父 agent、编辑前需重新读取文件。实现位于 fork-subagent.ts并在 agent.ts 的 worktree 模式下被调用注入。过期 worktree 自动清理fail-closed设计扫描.qwen/worktrees/匹配agent-{7hex}模式超过 30 天且无未推送提交则删除fail-closed 策略。worktreeCleanup.ts 的cleanupStaleAgentWorktrees()完整落地了这一策略EPHEMERAL_WORKTREE_PATTERNS只含AGENT_WORKTREE_SLUG_PATTERN——用户命名 worktree 永不清理它们由ExitWorktreeTool手动管理agent-前缀保留机制保证用户 slug 不会误匹配阈值STALE_WORKTREE_CUTOFF_MS 30 * 24 * 60 * 60 * 1000L40按目录 mtime 判断fail-closed 链条存在未提交 tracked 变更 → 跳过存在上游不可达提交 → 跳过git status检查失败 →假设为 dirty并跳过hasTrackedChanges 注释特别说明权限错误、文件系统异常必须留下日志痕迹且不可与「确实有变更」混淆脏检查用git status --porcelain --untracked-filesno既覆盖 staged/modified/conflicted旧实现曾漏掉 conflicted导致 merge 进行中的 worktree 被误扫又跳过大型仓库中最慢的 untracked 遍历删除竞态兜底目录已删但git branch -d因新提交失败时保留分支并 warn 日志提交仍可恢复。Arena 兼容保证Arena 内部不经过isolation参数创建 worktree此改动不触碰 Arena 代码路径——源码中setupWorktrees()批量接口与createUserWorktree()/createAgentWorktree()单会话接口在同一个 gitWorktreeService.ts 内并存互不干扰。设计文档还特别澄清了一个「无需改动」点review skill 使用独立机制路径.qwen/tmp/review-pr-n通过qwen review fetch-pr命令创建与通用 worktree 路径和机制完全不同不存在混淆。七、Phase C会话持久化与 UI 安全网目标worktree 状态在会话中断后可恢复用户在界面上始终知道自己在哪个 worktree 里退出会话时有安全提示。WorktreeSession sidecar 与 --resume 恢复会话状态以 JSON sidecar 文件持久化由 worktreeSessionService.ts 提供writeWorktreeSession/readWorktreeSession/clearWorktreeSession/isSessionRuntimeActive等能力。链路enter_worktree调用writeWorktreeSession()写入见上文第四节第 7 步exit_worktree在 slug 匹配时调用clearWorktreeSession()--resume/ 启动路径读取该字段恢复上下文headless 入口 nonInteractiveCli.ts 会在首条 prompt 前注入 worktree 上下文提示restoreWorktreeContext发出worktree_started/worktree_restored系统消息交互式 TUI 同理。Post-creation setuphooks 路径对齐创建 worktree 后自动执行git config core.hooksPath mainRepo/.git/hooks确保 worktree 内的提交与主仓库 hooks 行为一致。实现位于configureHooksPath()gitWorktreeService.ts在createUserWorktree()的成功路径中被 best-effort 调用L2150失败只记日志不中止创建。StatusLine 展示与退出对话框UIStateContext.tsx 新增activeWorktree字段含 WorktreeExitDialog 可见性状态从 session 状态读取在会话进入 / 退出 worktree 时更新Footer 在activeWorktree非空时内置展示⎇ branch (slug)行无需用户配置 statusline 脚本即可获得基本可见性配置项ui.hideBuiltinWorktreeIndicator可隐藏该行留给 custom statuslineStatusLineCommandInputpayload 携带worktree?: { slug, branch }供脚本使用WorktreeExitDialogCtrlC / CtrlD 第二次确认时若activeWorktree非空拦截退出并展示 keep / remove 选择对话框keep / remove 操作复用ExitWorktreeTool的路径originalHeadCommit在此处发挥作用——对话框用rev-list originalHeadCommit..HEAD统计本次会话新提交re-attach 场景下基线重新从 worktree 内部捕获避免把历史会话的提交都算作「本次工作」。八、Phase D--worktree启动标志、符号链接与 PR 引用设计文档指出三个功能放在同一阶段落地因为它们都挂在同一个启动入口上且 symlink / PR fetch 都必须在 worktree 创建之后立即执行单独拆分会重复改 bootstrap 序列。D-1--worktree [name]CLI 启动标志yargs 选项config.ts 的注释明确三种形态形式行为qwen --worktreebare flagyargs 传空字符串自动生成 slug{形容词}-{名词}-{6hex}工具路径为 4 位 hex此处为 6 位qwen --worktree my-name显式 slug沿用EnterWorktreeTool的 slug 校验规则qwen --worktreemy-name等价于上一种不提供短别名-w短别名只保留给最高频参数。核心实现是 worktreeStartup.ts 的setupStartupWorktree()L110在 argv 解析后、loadCliConfig()/Config构造前运行使process.chdir()的结果直接喂给 Config 的targetDir。与 Phase A 的行为差异设计文档明确要求在用户文档中说明Phase A 的EnterWorktreeTool不修改Config.targetDir而--worktree在启动期生效直接切换targetDir和process.cwd()——更强的隔离保证。re-attach 路径若 worktree 目录已存在例如之前--worktree foo退出时选了 Keep跳过git worktree addPR fetch 也跳过ref 首次运行已物化直接 chdir 进已有 worktree 并重新捕获 HEAD 基线。worktreeStartup.ts 的注释解释了为何 re-attach 必须用 worktree 内部 HEAD 而非启动 cwd 的 HEAD 作为originalHeadCommit。同时从已有 worktree 内部启动新 worktree 会被拒绝防嵌套L133-L141字面量pr-Nslug 允许 re-attach 到已有 PR worktree但不会凭空创建保留前缀在探测失败时重新生效。与--resume的优先级由于 session 存储以projectHash(process.cwd())为 key而--worktree在 resume picker 之前就 chdir「在 worktree X 启动的 session 从 worktree Y 内 resume」在架构上不可达。实际行为矩阵--resume状态--worktree状态结果无无普通会话无 worktree无有新 slug新建 worktree无有已存在的 slugre-attach 到已有 worktree有无恢复旧 worktreesidecar 命中则注入 reminder有sid 出自同一 worktree有同一 slugre-attach session 命中正常 resume有sid 出自 main checkout有任意 slugsession lookup 失败exit 1documented limitation有sid 出自 worktree X有slug Y, X ! Y同上session 跨 projectHash 不可寻跨 projectHash override 语义在 worktree / 主 checkout 的 session 之间转移需要 storage 锚定到 repo root 而非 cwd 派生的 projectHash属于未来 Config 重构范畴。D-2worktree.symlinkDirectories配置项schema新增worktree顶层 namespace在 settingsSchema.ts 中按字母序插在tools与ui之间{ worktree: { symlinkDirectories: [node_modules, dist, .turbo], }, }类型string[]默认undefinedopt-inrequiresRestart: false路径相对于主仓库根绝对路径或含..的路径被路径遍历守卫拒绝作用范围EnterWorktreeTool、AgentToolisolation: worktree、--worktreeCLI flag 创建的所有通用 worktreeArena worktree 不受影响。实现GitWorktreeService.symlinkConfiguredDirectories()gitWorktreeService.ts在createUserWorktree()成功后、紧跟configureHooksPath()调用。源码中的守卫比文档更细除拒绝绝对路径与..段外还拒绝.git内部路径、.qwen管理树内部路径、realpath 解析后逃逸 repo root 的源、以及目标父目录解析逃逸 worktree root 的符号链接链防御 committed-symlink 攻击。错误处理fail-open场景行为源目录不存在ENOENT静默跳过debug log目标路径已存在EEXIST静默跳过debug log不覆盖路径遍历../、绝对路径等拒绝该项debug log warn其他 I/O 错误debug log warn继续处理后续项worktree 创建本身不会因 symlink 失败而中止——与configureHooksPath()相同的 best-effort post-creation setup 原则。D-3PR 引用解析--worktree#N/ 全 URL支持形式parsePRReference()gitWorktreeService.ts形式解析后的 PR 号--worktree#123123--worktree #123123--worktree https://github.com/foo/bar/pull/123123--worktree https://gh.enterprise.com/foo/bar/pull/123?bazqux123企业版 带 queryslug 与分支命名slug 为pr-N特殊保留前缀与用户 slug 区分分支为worktree-pr-N沿用worktree-slug规则不采用pr-N直接命名避免与本地pr-N分支冲突。fetch 策略git fetch origin pull/N/head用FETCH_HEAD作为新 worktree 的 base。不依赖ghCLI——纯 git fetch支持任何 GitHub 实例公网或企业版只要origin指向 GitHub。选择head而非mergeref 的理由用户通常想看 PR 的实际改动。错误路径场景错误消息origin远程缺失--worktree#N requires an origin remote that points at GitHub.git fetch失败Failed to fetch PR #N: PR may not exist or origin remote is unreachable.网络超时30s同上加(timeout)origin不是 GitHub不做主动检查由git fetch自然失败PR worktree同样应用symlinkDirectories——用户期望在 PR 上立刻能跑测试依赖目录需要复用。安全与回滚fail-open vs fail-close 的边界symlink / hooks 失败不中止 worktree 创建Phase C 既定模式PR fetch 失败中止启动无 base ref 就无法创建 worktreeslug 校验失败中止启动cwd 切换的副作用切process.cwd()后相对路径参数如--prompt-file ./foo.txt解析会受影响。对策在setupStartupWorktree()入口处先做一次相对路径 normalize。九、配置与扩展项总览配置项类型用途阶段ui.hideBuiltinWorktreeIndicatorboolean隐藏 Footer 中内置⎇ worktree-… (…)行留给 custom statuslinePhase Cworktree.symlinkDirectoriesstring[]符号链接指定目录如node_modules到 worktree避免磁盘浪费Phase Dworktree.sparsePathsstring[]git sparse-checkout cone 模式大型 monorepo 只写入指定路径Future未实现Phase A / B 不新增任何配置项。用户文档见 docs/users/features/worktree.md。十、用户触发方式汇总方式示例阶段会话中明确请求用户说「在 worktree 中开始工作」→ 模型调用enter_worktreePhase AAgent 隔离模型为 subagent 设置isolation: worktreePhase BCLI 启动标志qwen --worktree my-feature/qwen --worktree#123Phase D无斜杠命令。会话中 worktree 的触发依赖用户明确提及isolation: worktree才是模型自主决策的场景。十一、Future 路线图以下功能面向更特定的场景当前不纳入排期待需求明确后再评估功能说明sparse checkoutworktree.sparsePaths配置项大型 monorepo 只 checkout 指定路径缩短创建时间和磁盘占用.worktreeinclude文件将 gitignore 的文件.env、secrets.json等自动复制进 worktreetmux 集成--worktree --tmux在新 tmux 窗口启动 worktree 会话设计文档在 D 阶段还留了三个开放问题--worktree-keep-on-exitflag建议先不加等反馈、symlinkDirectoriesper-project overridesettings 已有 user/workspace/project 三级合并无需特殊处理、PR fetch 取head还是mergeref沿用head。十二、延伸阅读仓库内关键实现索引设计文档docs/design/worktree.md用户文档docs/users/features/worktree.md工具实现enter-worktree.ts、exit-worktree.ts、工具名常量 tool-names.ts核心服务gitWorktreeService.tscreateUserWorktreeL2087、parsePRReferenceL1628、configureHooksPathL2202、symlinkConfiguredDirectoriesL2299、Arena 批量接口setupWorktreesL1016、worktreeSessionService.ts、worktreeCleanup.tsAgent 隔离agent.tsisolation参数与校验、fork-subagent.tsbuildWorktreeNotice、worktree-pin.tscaller-owned worktree pinCLI 启动路径worktreeStartup.ts、config.tsyargs 选项、settingsSchema.tsworktreenamespace、headless 注入 nonInteractiveCli.tsUI 状态UIStateContext.tsxactiveWorktree测试enter-worktree.test.ts、exit-worktree.test.ts、worktreeStartup.test.ts、worktreeCleanup.test.ts、gitWorktreeService.symlinks.integ.test.tssymlink 集成测试、gitWorktreeService.hooks.integ.test.tshooks 集成测试整体来看这套设计的工程取舍很清晰通用层与 Arena 层物理隔离保证存量功能零回归命名空间agent-/pr-保留前缀 会话 marker sidecar 三层机制解决「谁的 worktree、谁负责清理」的所有权问题fail-open 与 fail-closed 的边界setup 尽力而为、删除与 fetch 必须严格贯穿每一个删除与启动路径使「隔离工作环境」在 Agent 自主操作的场景下依然是可审计、可恢复、可回滚的。【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表