Git worktrees: несколько рабочих папок одного репозитория

Git worktrees: один репозиторий, несколько рабочих папок

Обычно один репозиторий - это одна папка с файлами и одна текущая ветка. Чтобы посмотреть другую ветку, приходится либо прятать правки в stash, либо коммитить «WIP», либо клонировать репозиторий второй раз. Git worktree даёт третий вариант: несколько рабочих папок, которые смотрят на одну и ту же базу коммитов и веток.

Типичные задачи, из-за которых заводят вторую папку:

  • Срочный баг посреди фичи

    Файлы изменены, вкладки открыты, dev-сервер запущен. Stash прячет работу и часто конфликтует на pop, второй clone тяжёлый. Нужна соседняя папка, которая не трогает первую.

  • Ревью не только по диффу

    Чужую ветку нужно поднять и прогнать, не бросая свою. Checkout в той же папке выкинет из контекста: пропадут незакоммиченные правки, процессы и открытые файлы.

  • Несколько AI-агентов сразу

    В одной папке агенты делят одни и те же файлы, перезаписывают правки и переключают ветку друг у друга. Каждому нужна своя папка и своя ветка.

  • Собрать несколько веток локально

    Документация из main и release/*, два тега на соседних портах. Второй clone дублирует историю, а worktree распаковывает только рабочие файлы.

В самом git механизм называется worktree (рабочее дерево). Слово «workspace» чаще встречается в IDE и GUI-клиентах, но речь об одном и том же: у одного репозитория появляется несколько независимых папок. Ниже - куда из такой папки уходит коммит, и симулятор основных команд.

Репозиторий, ветки, worktree

Слово «репозиторий» обычно значит папку проекта. Удобнее смотреть на три уровня:

  • Репозиторий - общая история. Все коммиты и все имена веток живут здесь сразу.
  • Ветки - ярлыки на коммиты: main, feature-x, agent/a.
  • Worktree - папка на диске, которая смотрит на одну ветку. Именно её файлы вы редактируете.

git worktree add добавляет ещё одну папку к тому же репозиторию. На каждую ветку смотрит не больше одной папки.

Типичная раскладка для параллельных агентов: вы в shop держите feature-x, два агента работают рядом на своих ветках, ответвлённых от вашей. Кликните на папку. Оранжевая рамка - куда уйдёт следующий коммит. После checkout папка переезжает под новую ветку.

Куда пишет коммит
Папка -
start Выберите папку агента и сделайте коммит: он попадёт в agent/a, не в вашу feature-x.

Могут ли два агента писать прямо в вашу feature-x? Нет. Каждый пишет в ту ветку, на которую смотрит его папка. Сменить ветку можно, но только на свободную.

  • Коммит всегда в свою текущую ветку. Агент A в shop-a пишет в agent/a. git commit не умеет целиться «в feature-x из соседней папки».
  • На любую свободную ветку сесть можно. main никто не открыл - агент делает git checkout main, папка переезжает под main, и следующие коммиты пойдут туда. feature-x занята вашей папкой, checkout git отклонит.
  • Чужую ветку можно читать, но не двигать. git merge feature-x из shop-a сработает: читает feature-x, пишет результат в agent/a.
  • В feature-x результаты собираете вы. Из shop: git merge agent/a, потом git merge agent/b. Либо через PR. На удалённый репозиторий правило «одна ветка - один worktree» не распространяется.
Что общее, а что своё. Общее: история и ветки. Коммит агента сразу виден вам в git log. Своё у папки: файлы на диске и текущая ветка. Поэтому чужой коммит не появится в ваших файлах, пока вы сами не сделаете merge или checkout.

Симулятор: команды и их эффект

Сверху команды репозитория: создать папку, список, prune. На карточке папки - то, что происходит внутри неё: новый файл, commit, checkout, merge, удаление. Отдельного переключателя «текущая папка» нет: нажали кнопку на карточке - терминал сам пишет cd.

Worktree simulator
Репозиторий
Terminalzsh

    
Дискпапки и файлы
Историяgit log --graph --decorate
start Один клон, одна папка, ветка main. Начните с git worktree add, потом создайте файл на карточке новой папки.

Что стоит попробовать в симуляторе:

  1. Создайте shop-hotfix. На её карточке в поле файла оставьте app/price.js или введите своё имя и нажмите touch. Файл появится только в этой папке, git пока его не знает (??). Затем commit: коммит сразу виден в графе справа, без push и pull.
  2. На карточке shop сделайте git merge hotfix: файл появится и здесь. Так результат соседней папки попадает в вашу ветку.
  3. Откройте feature-x в shop-review и попробуйте git checkout feature-x из shop. Git откажется: ветка занята.
  4. Создайте ещё один файл в shop-hotfix и попробуйте git worktree remove: без --force git защитит незакоммиченные правки.
  5. Удалите shop-old через rm -rf и посмотрите git worktree list: запись осталась и помечена как prunable.

Одна ветка - один worktree

Самое частое удивление: git не даёт сделать checkout ветки, которая уже открыта в другой папке. Причина не в вредности, а в том, что ветка - это указатель, а index и рабочие файлы у каждой папки свои. Если бы две папки смотрели на одну ветку, коммит в первой сдвинул бы указатель, а вторая папка вдруг оказалась бы «позади» собственной ветки с устаревшим index. Git не умеет надёжно разрешать такую ситуацию, поэтому запрещает её заранее.

$ git checkout feature-x
fatal: 'feature-x' is already used by worktree at '/Users/me/git/shop-review'

$ git branch -d feature-x
error: cannot delete branch 'feature-x' used by worktree at '/Users/me/git/shop-review'

Из этого правила есть два выхода:

  • Detached HEAD. Если нужен просто снимок коммита (посмотреть старый релиз, собрать версию, прогнать bisect), используйте git worktree add --detach <путь> <коммит>. Никакая ветка не занимается.
  • Новая ветка. Флаг -b создаёт ветку сразу при создании папки. Если написать git worktree add ../shop-hotfix без указания ветки, git сам создаст ветку с именем папки (shop-hotfix) от текущего HEAD.
Есть и --force. Он позволяет открыть одну ветку в двух папках, но тогда вы сами отвечаете за то, что после коммита во второй папке первая будет в рассинхроне. На практике это нужно только для восстановления после сбоев.

Три способа переключить контекст

Представьте сценарий: вы посреди фичи, файлы поменяны, ничего не закоммичено, и прилетает срочный баг в main. Переключите подход и сравните, что придётся сделать и что при этом окажется общим.

Срочный hotfix посреди незаконченной фичи

    Параллельные агенты

    Одна папка - это один index и один набор рабочих файлов. Если два агента правят один checkout одновременно, они наступают друг другу на staging area, конфликтуют в неотслеживаемых файлах и в худшем случае переключают ветку под ногами соседа. Git этого не запрещает: для него это просто «грязное» рабочее дерево, в котором кто-то ещё печатает.

    Worktree даёт каждому агенту отдельную папку и отдельную ветку при общей истории. Практичная раскладка выглядит так:

    ~/git/shop/            ← вы, ветка feature-x
    ~/git/shop-a/          ← агент A, git worktree add -b agent/a
    ~/git/shop-b/          ← агент B, git worktree add -b agent/b

    Что из этого следует на практике:

    • Свой index. git add одного агента не попадает в коммит другого.
    • Свой HEAD. Агент может делать checkout, rebase и даже bisect, не выкидывая вас из ветки. Правило «одна ветка - один worktree» здесь полезно: двум агентам нельзя сесть на одну и ту же ветку, и это лучше обнаружить сразу, чем разгребать общий указатель.
    • Свои рабочие файлы. Линтер, форматтер и codegen не дерутся за одни и те же пути.
    • Общая база. Коммит агента A сразу виден агенту B и вам: без clone, без push на origin «чтобы другой бот подтянул черновик».
    • В вашу ветку агенты не пишут. Как показано в диаграмме выше, их коммиты уходят в agent/a и agent/b. В feature-x они попадают только через ваш git merge из shop или через PR.

    Результат каждой сессии мержится как обычный PR. Это та же модель, что и hotfix посреди фичи, только «срочный баг» здесь - вторая задача, которую вы отдали другому агенту, пока первый ещё думает.

    Инструменты уже подстраиваются. У Claude Code есть флаг --worktree: агент сам создаёт связанную папку и работает там. В Cursor параллельные попытки тоже удобно разводить по отдельным worktree. В «Автоматизация жизни с AI агентами» я описывал ту же схему для OpenCode и Kimaki (/new-worktree): один агент, одна папка, одна ветка, отдельный канал в Discord. Worktree снова стали заметной темой не потому, что git изменился, а потому что параллельных писателей кода стало больше одного.

    Цена та же, что у любого linked worktree: node_modules, .env и порты в каждой папке свои. Для агентов это обычно закрывается тем же скриптом, которым вы поднимаете hotfix после git worktree add.

    Подводные камни

    Новый worktree - это чистый checkout

    В него попадают только файлы из git. Всё, что лежало рядом и игнорировалось, придётся создать заново: node_modules, vendor, .env, локальные базы, кэш сборки. Для JS-проектов установка зависимостей в каждой папке - основная цена подхода. Помогают менеджеры с общим кэшем (pnpm), а .env проще копировать скриптом сразу после worktree add.

    Порты, контейнеры и IDE

    Два запущенных приложения от одного проекта будут спорить за один порт, один docker-compose проект и одну локальную базу. Обычно решается переменной окружения или суффиксом имени проекта. IDE откроет вторую папку как отдельный проект, что чаще плюс, чем минус: индексы и настройки не перемешиваются.

    Не переносите и не удаляйте папки руками

    Связанный worktree и основной репозиторий хранят абсолютные пути друг к другу. После mv ссылки ломаются. Правильные команды: git worktree move для переноса, git worktree remove для удаления, git worktree prune для очистки записей о папках, которых уже нет, и git worktree repair, если пути всё-таки разошлись. Если worktree лежит на съёмном диске, git worktree lock запретит prune удалять запись, пока диск отключён.

    Общее - значит общее

    git stash - это просто ссылка refs/stash, поэтому список stash один на все папки. Хуки и config тоже одни. Если нужен конфиг для конкретной папки, включите extensions.worktreeConfig и используйте git config --worktree. Сабмодули с worktree работают, но каждый worktree держит собственную копию сабмодуля, и это отдельный источник сюрпризов.

    Bare-репозиторий как основа

    Популярная раскладка для тех, кто живёт в worktree постоянно: git clone --bare в папку .bare, а все ветки, включая main, открываются как связанные worktree рядом. Тогда нет «главной» папки, которая отличается от остальных. Единственная тонкость: у bare-клона по умолчанию не настроен refspec для fetch, и его нужно добавить руками: git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*".

    Шпаргалка

    КомандаЧто делаетЗаметка
    git worktree add <path> <branch>Открывает существующую ветку в новой папкеВетка не должна быть занята другим worktree
    git worktree add -b <new> <path> [<start>]Создаёт ветку и сразу открывает её<start> по умолчанию - текущий HEAD
    git worktree add <path>Создаёт ветку с именем папкиУдобно для одноразовых экспериментов
    git worktree add --detach <path> <commit>Открывает коммит без веткиДля bisect, сборки старых версий, просмотра тегов
    git worktree list [-v]Показывает все папки, их HEAD и статусОтмечает prunable, locked, detached
    git worktree remove [--force] <path>Удаляет папку и запись о нейБез --force откажется, если есть правки
    git worktree pruneУбирает записи о вручную удалённых папкахВетки при этом не удаляются
    git worktree move <path> <new-path>Переносит папку и обновляет ссылкиВместо mv
    git worktree lock / unlockЗащищает запись от pruneДля съёмных дисков и сетевых папок
    git worktree repairЧинит ссылки после переноса рукамиЗапускать из основной или из связанной папки
    Одна базаОбъекты, ветки, теги, stash, хуки и конфиг общие. Коммит в одной папке сразу виден в остальных.
    Свой снимокУ каждой папки свои HEAD, index и состояние незавершённых операций. Rebase в одной не мешает работе в другой.
    Одна ветка - одна папкаВетку нельзя открыть дважды. Для чтения истории берите detached HEAD, для работы - новую ветку.

    Если раньше вы решали «срочный баг посреди фичи» вторым клоном или stash, попробуйте один раз сделать git worktree add. Скорее всего, обратно возвращаться не захочется.