Рабочие деревья (Worktrees)
Изолируйте экспериментальную работу во временном git worktree , не покидая текущую сессию. Полезно, когда модель собирается внести масштабные изменения, которые вы хотите держать отдельно от основной рабочей копии, или если вы хотите, чтобы подагент работал в собственной песочнице.
Быстрый старт
Запуск сессии внутри рабочего дерева (флаг --worktree)
Если вы заранее знаете, что вся сессия должна выполняться внутри рабочего дерева, передайте --worktree при запуске:
# Автоматически сгенерированный слаг (например, tender-jemison-037f0a)
qwen --worktree
# Явное имя
qwen --worktree my-feature
# Форма с `=` (рекомендуется, если также передаётся позиционный prompt — см. подсказку ниже)
qwen --worktree=my-feature
# Ссылка на PR — извлекает refs/pull/<N>/head из `origin`
qwen --worktree=#4174
qwen --worktree https://github.com/QwenLM/qwen-code/pull/4174
# Продолжение предыдущей сессии --worktree — переподключается к существующему каталогу
qwen --resume <session-id> --worktree=my-featureПодсказка — просто
--worktreeбез значения, за которым следует позиционный prompt, может быть неоднозначным. Поскольку--worktreeпринимает необязательное значение,qwen --worktree "скажи привет"заставит yargs интерпретировать"скажи привет"как слаг (и отклонить его из-за пробела). Используйте один из вариантов:
qwen --worktree=my-feature "скажи привет"(всегда работает — явный слаг через=)qwen "скажи привет" --worktree(позиционный аргумент первым, флаг в конце → автоматический слаг)qwen --worktree --approval-mode yolo "скажи привет"(любой флаг между ними ставит якорь для базовой формы)
Подсказка —
qwen --resume --worktree foo(без ID сессии) при первом использовании показывает пустой список выбора. Список ограничен хранилищем сессий выбранного рабочего дерева; сессии, запущенные вне этого дерева, не отображаются. Чтобы возобновить сессию, которая была запущена внутриfoo, используйте напрямуюqwen --resume <id> --worktree foo— CLI переподключается к существующему каталогуfoo/, а не создаёт его заново.
process.cwd() и рабочее пространство модели переключаются на рабочее дерево до начала первого оборота. Дважды нажмите Ctrl+C — Диалог выхода предложит сохранить или удалить рабочее дерево.
Флаг --worktree нельзя комбинировать с --acp/--experimental-acp — для ACP-хостов (например, Zed) передайте путь к рабочему дереву как cwd в запросе loadSession/newSession.
Или попросите во время сессии
Кроме того, вы можете попросить Qwen Code на естественном языке создать рабочее дерево из текущей сессии:
> создай рабочее дерево с именем experiment-a
Создано рабочее дерево experiment-a на ветке worktree-experiment-a
.qwen/worktrees/experiment-aС этого момента модель будет направлять все правки файлов и команды оболочки через .qwen/worktrees/experiment-a/. Ваша исходная рабочая директория остаётся нетронутой.
Когда закончите:
> выйди из рабочего дерева и удали его
Удалено рабочее дерево experiment-a (ветка worktree-experiment-a)Если вы хотите вернуться позже, попросите выйти, сохранив дерево на диске:
> выйди из рабочего дерева, но сохрани его
Сохранено рабочее дерево experiment-a в .qwen/worktrees/experiment-aКогда используются рабочие деревья
Рабочие деревья активируются по четырём независимым путям:
| Триггер | Что происходит |
|---|---|
Вы запускаете с --worktree | CLI создаёт рабочее дерево до выполнения любого оборота модели и переходит в него сессией. Формы PR (#N, полный URL) предварительно извлекаются. |
| Вы явно просите рабочее дерево во время сессии | Модель вызывает enter_worktree; последующие правки файлов производятся внутри него. |
| Вы явно просите выйти | Модель вызывает exit_worktree с параметром keep или remove. |
| Модель порождает подагента с включённой изоляцией | Автоматически создаётся одноразовое рабочее дерево (agent-<hex>), которое удаляется, если у агента нет изменений. |
Два инструмента, доступных во время сессии (enter_worktree / exit_worktree), намеренно привязаны к явным фразам — обычные команды вроде «исправь эту ошибку» или «создай ветку» не вызовут их. Вы должны сказать что-то вроде «используй рабочее дерево», «создай рабочее дерево» или «в рабочем дереве». Флаг CLI --worktree не имеет такой защиты; при его наличии дерево всегда создаётся.
Что создаётся
Каждое рабочее дерево, управляемое Qwen, размещается в каталоге .qwen вашего проекта:
<repoRoot>/.qwen/worktrees/<slug>/ # Рабочий каталог
↳ ветка worktree-<slug> # Создаётся от вашей текущей ветки- Слаг — буквы, цифры, точка, подчёркивание, дефис; максимум 64 символа. Если вы не указали имя, автоматически генерируется слаг вида
<прилагательное>-<существительное>-<6-значный hex>(например,tender-jemison-037f0a). Ссылки на PR создают слагpr-<N>. - Ветка — всегда
worktree-<slug>, ответвляется от той ветки, которая у вас была отмечена при запросе рабочего дерева (не обязательно отHEADосновного дерева). Для рабочих деревьев PR ветка называетсяworktree-pr-<N>и основывается наFETCH_HEAD(верхушка PR со стороны GitHub), а не на вашей локальной ветке. - Хуки —
core.hooksPathрабочего дерева автоматически указывает на.husky/(предпочтительно) или.git/hooks/основного репозитория, чтобы коммиты внутри рабочего дерева по-прежнему запускали ваши существующие хуки pre-commit / commit-msg. - Опциональные симлинки — каталоги, перечисленные в
worktree.symlinkDirectories(см. Настройки), создаются как симлинки из основного репозитория в новое рабочее дерево, чтобы тяжёлые каталоги вродеnode_modulesможно было использовать повторно без переустановки. Путь к рабочему дереву общего назначения не настраивается — он должен находиться в<repoRoot>/.qwen/worktrees/, чтобы CLI мог найти его при перезапуске и при очистке устаревших деревьев. (Не связанный с этим параметрagents.arena.worktreeBaseDirуправляет только рабочими деревьями Agent Arena, которые используют отдельное дерево путей в~/.qwen/arena/.)
Footer and Status Line
Когда рабочее дерево активно, в нижнем колонтитуле (Footer) отображается тусклый индикатор на отдельной строке:
⎇ worktree-experiment-a (experiment-a)Если вы используете сценарий пользовательской строки состояния, он также получает объект worktree в JSON-полезной нагрузке, передаваемой через stdin:
{
"worktree": {
"name": "experiment-a",
"path": "/path/to/repo/.qwen/worktrees/experiment-a",
"branch": "worktree-experiment-a",
"original_cwd": "/path/to/repo",
"original_branch": "main"
}
}Поле полезной нагрузки присутствует только когда активно рабочее дерево, поэтому достаточно проверки на null (input.worktree?.name).
Если ваша пользовательская строка состояния уже отображает информацию о рабочем дереве, вы можете скрыть встроенную строку Footer, чтобы избежать дублирования — см. Настройки ниже.
Exit Dialog (Ctrl+C / Ctrl+D)
Двойное нажатие сочетания для выхода при активном рабочем дереве открывает Диалог выхода из рабочего дерева вместо закрытия CLI:
⎇ Активное рабочее дерево: "experiment-a" (worktree-experiment-a)
• 2 новых коммита в worktree-experiment-a
• 3 неиндексированных файла
Удаление рабочего дерева приведет к потере всего вышеуказанного.
Что вы хотите сделать?
○ Оставить рабочее дерево (выйти без удаления)
○ Удалить рабочее дерево и ветку (потеря 2 коммитов, 3 файлов)
○ Отмена (остаться в сессии)Диалог проверяет рабочее дерево при открытии (git status --porcelain + git rev-list <baseHEAD>..HEAD) и отображает оба счетчика, чтобы вы точно знали, что будет потеряно. ESC отменяет.
Если сам git status не срабатывает (например, поврежденный индекс, каталог рабочего дерева был удален из-под CLI), диалог показывает предупреждение ⚠ Could not measure worktree state, и счетчики могут быть ненадежными — выберите Оставить или Отмена, пока вы не диагностировали проблему с репозиторием.
--resume Restore
Привязка активного рабочего дерева сохраняется в боковой файл вместе с протоколом сессии:
<chatsDir>/<sessionId>.worktree.jsonКогда вы запускаете CLI с --resume <sessionId> (или выбираете сессию из /resume), последовательно выполняются три действия в режимах интерактивный TUI, headless -p и ACP/Zed:
- Боковой файл загружается, и проверяется, что каталог рабочего дерева все еще существует на диске.
- Если все в порядке, модель получает одноразовое напоминание при следующем запросе:
[Resumed] Active worktree: "<slug>" at <path> (branch: <branch>). Continue using this path for all file operations. - Если каталог рабочего дерева был удален между сессиями, устаревший боковой файл очищается автоматически — без ошибки, возобновление просто продолжается без контекста рабочего дерева.
Каждый режим выбирает свой механизм внедрения, но видимое пользователю поведение идентично:
| Режим | Механизм |
|---|---|
| Интерактивный (TUI) | Элемент истории INFO + префикс системного напоминания перед следующим пользовательским запросом. |
Headless (-p) | Префикс <system-reminder> перед запросом + системное событие worktree_restored в JSON-потоке вывода. |
| ACP (например, Zed) | Ожидающее уведомление, прикрепленное к следующему вызову prompt(). |
Модель не выполняет автоматический chdir в рабочее дерево — напоминание заставляет ее направлять изменения через путь рабочего дерева.
Sub-Agent Isolation
Инструмент agent принимает необязательный параметр isolation: "worktree". При его установке Qwen Code создает временное рабочее дерево в <repoRoot>/.qwen/worktrees/agent-<7hex>/ перед запуском под-агента, и:
- Нет изменений → рабочее дерево автоматически удаляется после завершения агента.
- Есть изменения → рабочее дерево сохраняется; его путь и ветка добавляются к результату агента, например:
Просмотрите diff и объедините или удалите его вручную.
…agent output… [worktree preserved: /path/to/.qwen/worktrees/agent-3f2a1b9 (branch worktree-agent-3f2a1b9)]
Два ограничения:
isolation: "worktree"требует указанияsubagent_type— форкнутые под-агенты (безsubagent_type) повторно используют полный контекст беседы родителя, поэтому их изоляция разделила бы намерение и рабочее дерево.- Фоновые агенты (
run_in_background: true) работают с изоляцией нормально; очистка выполняется, когда агент сообщает о завершении.
Automatic Stale Cleanup
Временные рабочие деревья агентов, пережившие сбой или завершение с --no-cleanup, удаляются при каждом запуске CLI с консервативными правилами безопасного отказа:
| Проверка | Поведение |
|---|---|
Slug должен соответствовать шаблону agent-<7hex> | Именованные рабочие деревья, созданные вами, никогда не затрагиваются. |
Время изменения каталога mtime > 30 дней | Более новые записи пропускаются. |
| Любые незафиксированные отслеживаемые изменения | Пропустить запись (не удалять). |
| Любой коммит, недостижимый из удаленного репозитория | Пропустить запись (не удалять). |
| Любая ошибка чтения состояния git | Пропустить запись (не удалять). |
Именованные рабочие деревья пользователя (слагы enter_worktree) никогда не очищаются автоматически — они остаются до тех пор, пока вы не запросите их удаление. |
Защитные механизмы при exit_worktree action="remove"
Три независимых защитных механизма срабатывают перед удалением каталога и ветки:
- Владение сессией — каждое рабочее дерево содержит дополнительный маркер с ID сессии, которая его создала. Попытка другой сессии удалить его отклоняется с понятной ошибкой, указывающей на
git worktree removeдля ручного обходного пути. - Грязное рабочее дерево — несохранённые отслеживаемые или неотслеживаемые изменения блокируют удаление. Укажите
discard_changes: true, чтобы переопределить. (Обход требует явного подтверждения пользователя —action: "remove"никогда не одобряется автоматически в режиме AUTO_EDIT.) - Неслитые коммиты — коммиты в
worktree-<slug>, на которые не ссылается ни одна другая локальная ветка или удалённый реф, безусловно блокируют удаление; флага «отбросить коммиты» не существует, поскольку потеря закоммиченной работы редко соответствует намерениям пользователей. Сначала выполните слияние, отправку или переименование ветки в другом месте.
Те же три защитных механизма применяются к кнопке WorktreeExitDialog → Remove.
Настройки
Два параметра формируют работу с рабочими деревьями общего назначения:
| Ключ | Тип | По умолчанию | Эффект |
|---|---|---|---|
ui.hideBuiltinWorktreeIndicator | boolean | false | Скрывает встроенную строку нижнего колонтитула ⎇ worktree-… (…). Поле worktree всё равно передаётся в пользовательские скрипты строки состояния. Установите true, только если ваша строка состояния уже отображает рабочее дерево — иначе вы потеряете все визуальные подсказки. |
worktree.symlinkDirectories | string[] | undefined | Каталоги в основном репозитории, которые нужно симлинковать в каждое рабочее дерево общего назначения при создании. Пути указываются относительно корня репозитория; абсолютные пути и любые записи, содержащие .., отклоняются. Отсутствующие источники и существующие цели молча пропускаются (без перезаписи). |
Пример:
// ~/.qwen/settings.json or <repo>/.qwen/settings.json
{
"worktree": {
"symlinkDirectories": ["node_modules", ".turbo", "dist"],
},
}Применяется ко ВСЕМ путям создания рабочих деревьев: флаг --worktree, инструмент enter_worktree и agent isolation: "worktree".
Настройки, не связанные с рабочими деревьями общего назначения, но о которых стоит знать:
agents.arena.worktreeBaseDir— управляет размещением рабочих деревьев Agent Arena (по умолчанию~/.qwen/arena). Не влияет на рабочие деревья общего назначения, которые всегда находятся в<repoRoot>/.qwen/worktrees/.
Схемы для worktree.sparsePaths пока нет — это пункт дорожной карты (см. Ограничения).
Справочник по инструментам
enter_worktree
{ "name": "experiment-a" }| Поле | Тип | Обязательно | Примечания |
|---|---|---|---|
name | string | нет | Слаг. Буквы, цифры, точка, подчёркивание, дефис; максимум 64 символа. Автоматически генерируется, если не указан. |
Отказывается запускаться, когда:
- CLI не находится в git-репозитории.
- Текущая рабочая директория уже находится внутри
.qwen/worktrees/(вложенные рабочие деревья не поддерживаются).
exit_worktree
{ "name": "experiment-a", "action": "remove", "discard_changes": false }| Поле | Тип | Обязательно | Примечания |
|---|---|---|---|
name | string | да | Должен совпадать с слагом, использованным в enter_worktree. |
action | "keep" | "remove" | да | keep сохраняет каталог + ветку; remove удаляет и то, и другое. |
discard_changes | boolean | только когда action="remove" и дерево грязное | Переопределяет защиту грязного дерева. Не действует при action="keep". |
action: "remove" всегда запрашивает подтверждение, в том числе в режиме одобрения AUTO_EDIT — это считается деструктивной операцией оболочки, а не информационным инструментом.
agent — параметр isolation
{
"subagent_type": "my-agent",
"description": "…",
"prompt": "…",
"isolation": "worktree"
}| Поле | Тип | Обязательно | Примечания |
|---|---|---|---|
isolation | "worktree" | нет | Запускает агента в новом рабочем дереве agent-<7hex>. Требует указания subagent_type (форки не поддерживаются). |
| Полный список инструментов агентов см. в разделе Sub-Agents. |
Справочник по CLI
--worktree [name | #N | url]
qwen --worktree # автоматическая генерация slug
qwen --worktree my-feature # явный slug
qwen --worktree=my-feature # форма с =
qwen --worktree=#123 # ссылка на PR
qwen --worktree https://github.com/owner/repo/pull/123 # URL PR| Ввод | Результат |
|---|---|
| Пустой флаг (без значения) | Авто-слаг <adjective>-<noun>-<6hex>, ветка worktree-<slug>, базовая ветка = текущая. |
| Простой slug | Ветка worktree-<slug>, базовая ветка = текущая. Проверка slug: буквы/цифры/точка/подчёркивание/дефис, макс. 64 символа. |
#N или <github-url>/pull/N | Слаг pr-<N>, ветка worktree-pr-<N>, базовая ветка = FETCH_HEAD после git fetch origin pull/<N>/head (таймаут 30 с). |
--worktree нельзя сочетать с --acp / --experimental-acp.
Когда --worktree используется вместе с --resume <session-id>, приоритет у worktree: сохранённая в сессии рабочая директория (если есть) переопределяется, а в stderr и в первом напоминании сообщается о переопределении.
В интерактивном (TUI) и фоновом (-p) режимах worktree создаётся автоматически, и сессия переходит в эту директорию перед первым запросом.
Режимы сбоя при получении PR (код возврата != 0, worktree не создан):
| Причина | Фрагмент сообщения |
|---|---|
Отсутствует удалённый origin | требуется удалённый репозиторий "origin", указывающий на GitHub |
PR не существует на origin | Не удалось получить PR #<N>: PR не существует на origin |
| Таймаут сети 30 с | Не удалось получить PR #<N>: таймаут (30 с) |
| Номер PR вне диапазона / ноль | Некорректный номер PR |
Ограничения
В текущей фазе намеренно не реализованы следующие возможности:
- Нет частичного клонирования (sparse checkout). Большие монорепозитории получают полное дерево. (
worktree.sparsePathsв планах.) - Нет интеграции с tmux. CLI не создаёт сессии worktree в новых окнах tmux.
- Worktree считаются отдельными “проектами” для хранения сессий. Сессии, запущенные с
--worktree foo, сохраняются в каталоге чатов этого worktree; чтобы возобновить их позже, нужно снова указать--worktree foo. Сессии, запущенные без--worktree, сохраняются в основном репозитории и не отображаются в списке возобновления worktree. - Нет переопределения сессий между slug.
qwen --resume <sid> --worktree second, где<sid>был создан с--worktree first, не найдёт сессию — сессии и worktree жестко привязаны черезprojectHash(cwd). Чтобы сменить worktree для существующей сессии, нужно выйти, затем запустить с новым--worktreeи новым запросом. Будущее архитектурное изменение (привязка хранилища к корню репозитория вместоcwd) снимет это ограничение. - Mid-session
enter_worktreeНЕ меняетprocess.cwd()илиConfig.targetDir. Этот инструмент использует соглашение, ограниченное контекстом модели (см. Sub-Agents). Только флаг--worktreeпри запуске фактически меняет рабочую директорию процесса. - Относительные пути в других аргументах вычисляются ДО перехода в worktree. Флаги, принимающие пути (
--mcp-config,--openai-logging-dir,--json-file,--input-file,--telemetry-outfile,--include-directories), нормализуются в абсолютные пути относительно каталога запуска, если установлен--worktree. Другие аргументы в форме путей, не включённые в этот список, всё равно вычисляются относительно рабочего каталога worktree — используйте абсолютные пути для надёжности.
Дорожная карта — в docs/design/worktree.md.
Устранение неполадок
Внизу не появляется индикатор worktree, хотя я только что его создал.
Проверьте, что ui.hideBuiltinWorktreeIndicator не установлен в true. Также убедитесь, что slug не пуст в сообщении об успешном создании инструмента.
--resume не восстанавливает мой worktree.
Проверьте, существует ли файл <chatsDir>/<sessionId>.worktree.json. CLI автоматически удаляет боковой файл, если каталог worktree исчез, поэтому отсутствие бокового файла и каталога — это нормальное состояние “нет worktree для восстановления”, не ошибка. Запустите с --debug и поищите restoreWorktreeContext, чтобы увидеть причину.
exit_worktree сообщает “создано другой сессией”.
Это защита владельца сессии. Возобновите исходную сессию и выйдите из неё, либо выполните предложенную команду git worktree remove … вручную.
Старые worktree agent-<hex> продолжают накапливаться.
Порог в 30 дней является консервативным; очистите вручную с помощью git worktree list && git worktree remove <path> или подождите — следующий запуск CLI после истечения 30-дневного срока удалит их, если они чистые и отправлены.