Worktrees
Изолируйте экспериментальную работу во временном git worktree , не покидая текущую сессию. Полезно, когда модель собирается вносить масштабные изменения, которые вы хотите держать отдельно от основной рабочей копии, или когда нужно запустить субагента в отдельной песочнице.
Быстрый старт
Запуск сессии внутри worktree (флаг --worktree)
Если вы заранее знаете, что вся сессия должна выполняться внутри worktree, передайте --worktree при запуске:
# Автоматически сгенерированный slug (например, tender-jemison-037f0a)
qwen --worktree
# Явно указанное имя
qwen --worktree my-feature
# Форма с `=` (рекомендуется, если также передаётся позиционный промпт — см. подсказку ниже)
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с последующим позиционным промптом неоднозначен. Поскольку--worktreeпринимает необязательное значение,qwen --worktree "say hi"заставляет yargs воспринять"say hi"как slug (и отклонить его из-за пробела). Используйте один из вариантов:
qwen --worktree=my-feature "say hi"(всегда работает — явный slug через=)qwen "say hi" --worktree(позиционный промпт первым, флаг в конце → авто-slug)qwen --worktree --approval-mode yolo "say hi"(любой флаг между ними фиксирует голую форму)
Подсказка —
qwen --resume --worktree foo(без ID сессии) при первом использовании показывает пустой список выбора. Список ограничивается хранилищем сессий выбранного worktree; сессии, запущенные вне этого worktree, не отображаются. Чтобы возобновить сессию, которая была запущена внутриfoo, используйтеqwen --resume <id> --worktree fooнапрямую — CLI переподключается к существующему каталогуfoo/, не создавая его заново.
process.cwd() и рабочее пространство модели переключаются на worktree до выполнения первого шага. Выйдите дважды с помощью Ctrl+C, и Диалог выхода предложит сохранить или удалить worktree.
Флаг --worktree нельзя комбинировать с --acp/--experimental-acp — для ACP-хостов (например, Zed) передавайте путь к worktree как cwd в запросе loadSession/newSession.
Или попросите mid-session
Альтернативно, попросите Qwen Code на естественном языке создать worktree из текущей сессии:
> создай worktree с именем experiment-a
Worktree experiment-a создан на ветке worktree-experiment-a
.qwen/worktrees/experiment-aНачиная с этого момента модель будет направлять все редактирования файлов и команды оболочки через .qwen/worktrees/experiment-a/. Ваш исходный рабочий каталог остаётся нетронутым.
Когда закончите:
> выйди из worktree и удали его
Worktree experiment-a удалён (ветка worktree-experiment-a)Если вы хотите вернуться позже, попросите выйти, оставив worktree на диске:
> выйди из worktree, но сохрани его
Worktree experiment-a сохранён по пути .qwen/worktrees/experiment-aКогда Worktrees используются
Worktrees активируются по четырём независимым сценариям:
| Триггер | Что происходит |
|---|---|
Запуск с --worktree | CLI создаёт worktree до первого шага модели и переключает сессию в него. PR-формы (#N, полный URL) сначала выполняют fetch. |
| Явный запрос worktree в середине сессии | Модель вызывает enter_worktree; последующие редактирования файлов выполняются внутри него. |
| Явный запрос на выход | Модель вызывает exit_worktree с параметром keep или remove. |
| Модель запускает субагента с включённой изоляцией | Автоматически создаётся одноразовый worktree (agent-<hex>), который удаляется, если у агента нет изменений. |
Два инструмента mid-session (enter_worktree / exit_worktree) намеренно привязаны к явным формулировкам — фразы “исправь эту ошибку” или “создай ветку” не запускают их. Нужно сказать что-то вроде “используй worktree”, “создай worktree” или “в worktree”. Флаг CLI --worktree такой защиты не имеет — он всегда создаёт worktree при наличии.
Что создаётся
Каждый управляемый Qwen worktree размещается в каталоге .qwen вашего проекта:
<repoRoot>/.qwen/worktrees/<slug>/ # Рабочий каталог
↳ ветка worktree-<slug> # Создаётся от текущей ветки- Slug — буквы, цифры, точка, подчёркивание, дефис; макс. 64 символа. Если имя не указано, автоматически генерируется slug вида
<adjective>-<noun>-<6hex>(например,tender-jemison-037f0a). Ссылки на PR дают slugpr-<N>. - Ветка — всегда
worktree-<slug>, ответвляется от той ветки, которая у вас была активна при запросе worktree (не обязательноHEADосновного рабочего дерева). Для PR-ворктри ветка называетсяworktree-pr-<N>и базируется наFETCH_HEAD(верхушка PR на стороне GitHub), а не на вашей локальной ветке. - Хуки —
core.hooksPathworktree автоматически указывается на основной репозиторий.husky/(предпочтительно) или.git/hooks/, чтобы коммиты внутри worktree по-прежнему запускали ваши существующие pre-commit / commit-msg хуки. - Необязательные симлинки — каталоги, перечисленные в
worktree.symlinkDirectories(см. Настройки), симлинкуются из основного репозитория в новый worktree, чтобы тяжёлые каталоги типаnode_modulesможно было переиспользовать без переустановки.
Путь к worktree общего назначения не настраивается — он должен находиться в <repoRoot>/.qwen/worktrees/, чтобы CLI мог найти его при перезапуске и при очистке устаревших. (Несвязанная настройка agents.arena.worktreeBaseDir управляет только worktree Agent Arena, которые используют отдельное дерево путей в ~/.qwen/arena/.)
Футер и строка состояния
Когда worktree активен, в футере отображается затемнённый индикатор на отдельной строке:
⎇ 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"
}
}Поле присутствует только когда worktree активен, поэтому достаточно проверки на null (input.worktree?.name).
Если ваша строка состояния уже отображает информацию о worktree, вы можете скрыть встроенную строку футера, чтобы избежать дублирования — см. Настройки ниже.
Диалог выхода (Ctrl+C / Ctrl+D)
Двойное нажатие комбинации выхода при активном worktree открывает Диалог выхода из worktree вместо закрытия CLI:
⎇ Активный worktree: "experiment-a" (worktree-experiment-a)
• 2 новый коммит(а) на worktree-experiment-a
• 3 не закоммиченных файла
Удаление worktree приведёт к потере всего вышеуказанного.
Что вы хотите сделать?
○ Сохранить worktree (выйти без удаления)
○ Удалить worktree и ветку (будет отброшено 2 коммита, 3 файла)
○ Отмена (остаться в сессии)Диалог проверяет состояние worktree при открытии (git status --porcelain + git rev-list <baseHEAD>..HEAD) и показывает оба значения, чтобы вы точно знали, что будет отброшено. Клавиша ESC отменяет действие.
Если сам git status завершается ошибкой (например, повреждённый индекс, каталог worktree был удалён из-под CLI), диалог показывает предупреждение ⚠ Не удалось определить состояние worktree, и значения могут быть ненадёжными — выберите Сохранить или Отмена, пока не диагностируете проблему с репозиторием.
Восстановление через --resume
Привязка активного worktree сохраняется в файле-сайдкаре рядом с транскриптом сессии:
<chatsDir>/<sessionId>.worktree.jsonКогда вы запускаете CLI с --resume <sessionId> (или выбираете сессию из /resume), три действия выполняются единообразно во всех режимах — интерактивный TUI, безголовый -p и ACP/Zed:
- Сайдкар загружается, и проверяется, что каталог worktree всё ещё существует на диске.
- Если он жив, модель получает одноразовое напоминание в самом следующем промпте:
[Возобновлено] Активный worktree: "<slug>" по пути <path> (ветка: <branch>). Продолжайте использовать этот путь для всех файловых операций. - Если каталог worktree был удалён между сессиями, устаревший сайдкар автоматически очищается — без ошибки, возобновление просто продолжается без контекста worktree.
Каждый режим выбирает свой механизм внедрения, но видимое пользователю поведение идентично:
| Режим | Механизм |
|---|---|
| Интерактивный (TUI) | Элемент истории INFO + префикс-напоминание в системе в следующем пользовательском промпте. |
Безголовый (-p) | Префикс <system-reminder> в промпте + системное событие JSON worktree_restored в выходном потоке. |
| ACP (например, Zed) | Ожидающее уведомление, прикреплённое к следующему вызову prompt(). |
Модель не переключается автоматически в worktree через chdir — напоминание просто заставляет её направлять правки по пути worktree.
Изоляция субагента
Инструмент agent принимает необязательный параметр isolation: "worktree". Когда он установлен, Qwen Code создаёт эфемерный worktree в <repoRoot>/.qwen/worktrees/agent-<7hex>/ до запуска субагента, и:
- Нет изменений → worktree автоматически удаляется по завершении работы агента.
- Есть изменения → worktree сохраняется; его путь и ветка добавляются к результату агента, например:
Просмотрите diff и объедините или удалите вручную.
…вывод агента… [worktree сохранён: /path/to/.qwen/worktrees/agent-3f2a1b9 (ветка worktree-agent-3f2a1b9)]
Два ограничения:
isolation: "worktree"требуетsubagent_type— разветвлённые субагенты (безsubagent_type) повторно используют полный контекст разговора родителя, поэтому их изоляция разделила бы намерение и рабочее дерево.- Фоновые агенты (
run_in_background: true) корректно работают с изоляцией; очистка запускается, когда агент сообщает о завершении.
Автоматическая очистка устаревших
Эфемерные агентские worktree, пережившие сбой или завершение с --no-cleanup, удаляются при каждом запуске CLI с консервативными правилами fail-closed:
| Проверка | Поведение |
|---|---|
Slug должен соответствовать шаблону agent-<7hex> | Именованные worktree, созданные вами, никогда не трогаются. |
mtime каталога > 30 дней | Более новые записи пропускаются. |
| Любое незакоммиченное отслеживаемое изменение | Пропустить запись (не удалять). |
| Любой коммит, недоступный с удалённого репозитория | Пропустить запись (не удалять). |
| Любая ошибка чтения git-состояния | Пропустить запись (не удалять). |
Именованные пользовательские worktree (slug из enter_worktree) никогда не очищаются автоматически — они остаются, пока вы не попросите их удалить.
Защитные проверки в exit_worktree action="remove"
Три независимые проверки выполняются перед удалением каталога и ветки:
- Владение сессией — каждый worktree имеет маркер-сайдкар с ID создавшей его сессии. Если другая сессия пытается его удалить, запрос отклоняется с чёткой ошибкой, указывающей на
git worktree removeкак ручной выход. - Грязное рабочее дерево — незакоммиченные отслеживаемые или неотслеживаемые изменения блокируют удаление. Передайте
discard_changes: trueдля переопределения. (Обход требует явного подтверждения пользователя —action: "remove"никогда не подтверждается автоматически в режиме AUTO_EDIT.) - Неслитые коммиты — коммиты на
worktree-<slug>, на которые не ссылается ни одна другая локальная ветка или удалённая ссылка, блокируют удаление безусловно; флага “отбросить коммиты” нет, потому что потеря закоммиченной работы редко соответствует намерениям пользователя. Сначала объедините, отправьте или переименуйте ветку в другом месте.
Те же три проверки применяются к кнопке Удалить в WorktreeExitDialog.
Настройки
Две настройки формируют работу с worktree общего назначения:
| Ключ | Тип | По умолчанию | Эффект |
|---|---|---|---|
ui.hideBuiltinWorktreeIndicator | boolean | false | Скрывает встроенную строку футера ⎇ worktree-… (…). Поле worktree по-прежнему передаётся пользовательским скриптам строки состояния. Установите true только если ваша строка состояния уже отображает worktree — иначе вы потеряете всю UI-подсказку. |
worktree.symlinkDirectories | string[] | undefined | Каталоги в основном репозитории, которые нужно симлинговать в каждый worktree общего назначения при создании. Пути задаются относительно корня репозитория; абсолютные пути и любые записи, содержащие .., отклоняются. Отсутствующие источники и существующие цели молча пропускаются (без перезаписи). |
Пример:
// ~/.qwen/settings.json или <repo>/.qwen/settings.json
{
"worktree": {
"symlinkDirectories": ["node_modules", ".turbo", "dist"],
},
}Применяется ко всем путям создания worktree: флаг --worktree, инструмент enter_worktree и agent isolation: "worktree".
Настройки, не связанные с worktree общего назначения, но полезные к сведению:
agents.arena.worktreeBaseDir— управляет размещением worktree Agent Arena (по умолчанию~/.qwen/arena). Не влияет на worktree общего назначения, которые всегда находятся в<repoRoot>/.qwen/worktrees/.
Схемы для worktree.sparsePaths пока нет — это элемент дорожной карты (см. Ограничения).
Справочник по инструментам
enter_worktree
{ "name": "experiment-a" }| Поле | Тип | Обязательно | Примечания |
|---|---|---|---|
name | string | нет | Slug. Буквы, цифры, точка, подчёркивание, дефис; макс. 64 символа. Авто-генерация, если не указан. |
Отказывается выполняться, когда:
- CLI находится не в git-репозитории.
- Текущий рабочий каталог уже находится внутри
.qwen/worktrees/(вложенные worktree запрещены).
exit_worktree
{ "name": "experiment-a", "action": "remove", "discard_changes": false }| Поле | Тип | Обязательно | Примечания |
|---|---|---|---|
name | string | да | Должен совпадать с slug, использованным в 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" | нет | Запускает агента в новом worktree agent-<7hex>. Требует установки subagent_type (без форков). |
См. Субагенты для остальной части справочника по инструменту агента.
Справочник по 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| Вход | Результат |
|---|---|
| Голый флаг (без значения) | Авто slug <adjective>-<noun>-<6hex>, ветка worktree-<slug>, базовая = текущая ветка. |
| Простой slug | Ветка worktree-<slug>, базовая = текущая ветка. Валидация slug: буквы/цифры/точка/подчёркивание/дефис, макс. 64 симв. |
#N или <github-url>/pull/N | Slug pr-<N>, ветка worktree-pr-<N>, базовая = FETCH_HEAD после git fetch origin pull/<N>/head (тайм-аут 30 с). |
--worktree нельзя комбинировать с --acp / --experimental-acp.
Когда --worktree комбинируется с --resume <session-id>, приоритет у worktree: сохранённый worktree возобновляемой сессии (если есть) переопределяется, и в stderr выводится строка + напоминание в первом промпте об этом переопределении.
Для интерактивного (TUI) и безголового (-p) режимов worktree создаётся автоматически, и сессия переключается в него до первого шага.
Режимы ошибок при fetch PR (код выхода != 0, worktree не создан):
| Причина | Фрагмент сообщения |
|---|---|
Отсутствует удалённый origin | требуется удалённый "origin", указывающий на GitHub |
| PR не существует на origin | Не удалось получить PR #<N>: PR не существует на origin |
| Тайм-аут сети 30 с | Не удалось получить PR #<N>: тайм-аут через 30 с |
| Номер PR вне диапазона / ноль | Недопустимый номер PR |
Ограничения
Следующие пункты намеренно не реализованы в текущей фазе:
- Нет разреженной выгрузки. Большие монорепозитории загружают полное дерево. (
worktree.sparsePaths— элемент дорожной карты.) - Нет интеграции с tmux. CLI не запускает сессии worktree в новых окнах tmux.
- Worktree — отдельные “проекты” для хранения сессий. Сессии, запущенные с
--worktree foo, сохраняются в каталоге chats этого worktree; чтобы возобновить их позже, нужно снова передать--worktree foo. Сессии, запущенные без--worktree, сохраняются в основной рабочей копии и не появятся в выборе возобновления этого worktree. - Нет переопределения сессии между slug.
qwen --resume <sid> --worktree second, где<sid>был создан с--worktree first, не сможет найти сессию — сессии и worktree жёстко привязаны черезprojectHash(cwd). Чтобы сменить worktree для существующей сессии, нужно выйти, затем перезапустить с новым--worktreeи свежим промптом. Будущее архитектурное изменение (привязка хранилища к корню репозитория вместоcwd) снимет это ограничение. - Вызов
enter_worktreemid-session НЕ переключаетprocess.cwd()илиConfig.targetDir. Этот инструмент использует соглашение только контекста модели (см. Субагенты). Только флаг запуска--worktreeдействительно переключает рабочий каталог процесса. - Относительные пути в других аргументах разрешаются ДО переключения worktree. Флаги, принимающие пути (
--mcp-config,--openai-logging-dir,--json-file,--input-file,--telemetry-outfile,--include-directories), нормализуются в абсолютные пути относительно cwd запуска, когда установлен--worktree. Другие поля argv в форме пути, не входящие в этот список, всё равно разрешаются относительно cwd worktree — используйте абсолютные пути для надёжности. Отслеживайте дорожную карту вdocs/design/worktree.md.
Устранение неполадок
На панели Footer не отображается индикатор worktree, хотя я только что его создал.
Проверьте, что ui.hideBuiltinWorktreeIndicator не установлено в true. Также убедитесь, что slug не пуст в сообщении об успешном выполнении инструмента.
--resume не восстанавливает мой worktree.
Проверьте, существует ли <chatsDir>/<sessionId>.worktree.json. CLI автоматически удаляет sidecar, когда каталог worktree удалён, поэтому отсутствие sidecar и каталога — это нормальное состояние “нет worktree для восстановления”, а не ошибка. Запустите с флагом --debug и выполните grep по restoreWorktreeContext, чтобы увидеть причину.
exit_worktree сообщает “created by a different session”.
Это защита владения сессией. Возобновите исходную сессию и выйдите из неё, или выполните предложенную команду git worktree remove … вручную.
Устаревшие worktree agent-<hex> продолжают накапливаться.
30-дневный порог является консервативным; выполните очистку вручную с помощью git worktree list && git worktree remove <path> или подождите — следующий запуск CLI после истечения 30 дней удалит их, если они чистые и отправлены в удалённый репозиторий.