git worktree: несколько веток одновременно

Как держать несколько веток одного репозитория открытыми в разных папках одновременно — без stash, без второго clone.

git worktree — механизм, позволяющий одному репозиторию иметь несколько рабочих деревьев: каждая дополнительная папка-worktree держит свою ветку и свой набор файлов, но все они делят одну общую базу объектов в .git/.

Зачем это нужно на практике

Вы пишете большую фичу, рабочее дерево забито недоделанными правками — и тут прилетает срочный баг на проде. Классика: git stash, переключение на main, фикс, обратное переключение, git stash pop и молитва, что ничего не конфликтует. Или второй git clone — но это полная копия истории, лишние гигабайты и рассинхрон веток.

Worktree решает это чисто: вы создаёте отдельную папку для хотфикса, чините баг там, а основная папка с фичей остаётся нетронутой. Обе папки видят одни и те же коммиты, ветки и stash — потому что объектная база общая. Переключение между задачами — это просто cd в другую директорию.

Создание и список worktree

Основная команда — git worktree add. Ей нужны путь к новой папке и ветка, которую туда выложить.

# создать worktree для существующей ветки
git worktree add ../proj-hotfix hotfix/login

# создать НОВУЮ ветку и worktree одной командой
git worktree add -b feature/export ../proj-export origin/main

Флаг -b feature/export заводит новую ветку от origin/main и сразу выкладывает её в папку ../proj-export. После этого у вас две полноценные рабочие папки: исходная и новая. Посмотреть все деревья и их ветки:

git worktree list
# /home/me/proj          a1b2c3d [feature/big]
# /home/me/proj-hotfix   d4e5f6a [hotfix/login]
# /home/me/proj-export   0099aab [feature/export]

Типичный сценарий с хотфиксом целиком:

git worktree add ../proj-hotfix -b hotfix/crash origin/main
cd ../proj-hotfix
# ... правим, коммитим, пушим ...
git commit -am "fix: null pointer on logout"
git push -u origin hotfix/crash
cd ../proj          # вернулись к фиче — она нетронута

Удаление и уборка

Закончив с веткой, удалите её дерево. Сначала уберите папку через Git (он почистит служебные ссылки), затем при желании удалите саму ветку:

git worktree remove ../proj-hotfix     # удалить дерево
git branch -d hotfix/crash             # удалить слитую ветку

Если папку случайно удалили вручную (через rm -rf), у Git останутся «висячие» записи. Подчистить их:

git worktree prune        # убрать ссылки на исчезнувшие деревья
git worktree list         # проверить, что мусора не осталось

Полезный флаг для долгоживущих деревьев — lock: он защищает worktree на съёмном диске или сетевой шаре от автоматической очистки prune:

git worktree lock ../proj-on-usb --reason "review build"
git worktree unlock ../proj-on-usb

Как это работает под капотом

Главный репозиторий — это папка с подкаталогом .git/. Когда вы добавляете worktree, Git создаёт в новой папке не каталог, а файл .git с одной строкой gitdir: /путь/к/основному/.git/worktrees/proj-export. Внутри .git/worktrees/<имя>/ лежат отдельные HEAD, индекс и ссылки этого дерева. А вот сама база объектов (коммиты, деревья, блобы) и большинство ссылок — общие. Отсюда два следствия.

Во-первых, дисковая экономия: новый worktree весит ровно как выложенные файлы плюс крошечные служебные данные, а не как вся история. Во-вторых, важное ограничение: одну и ту же ветку нельзя выложить в два дерева сразу. Если ветка main уже занята основным деревом, git worktree add ../x main вернёт ошибку — иначе два дерева могли бы двигать один и тот же указатель ветки и конфликтовать. Обходится созданием отдельной ветки (-b) или флагом --detach для одноразового detached-дерева.

git worktree add --detach ../proj-ci v2.0.0  # одноразовое дерево на теге

Типичные сценарии

Worktree окупается в нескольких повторяющихся ситуациях. Срочный хотфикс посреди фичи — отдельное дерево от origin/main, починили, запушили, вернулись; недоделанная фича всё это время лежит нетронутой в своей папке. Ревью чужого PR без потери контекста — выложили ветку коллеги в ../proj-review, собрали и погоняли её локально, пока ваша работа открыта рядом. Долгая сборка или прогон тестов — пока CI-дерево на теге компилирует релиз, вы спокойно пишете код в основном дереве, не дожидаясь окончания. Сравнение двух версий бок о бок — открыли v1 и v2 в соседних папках и сверяете поведение в двух запущенных экземплярах приложения. Во всех случаях выигрыш один: переключение задачи — это cd, а не «спрячь-переключись-восстанови».

Под несколько активных деревьев удобна общая схема именования папок: проект-фича, проект-hotfix, проект-review рядом с основной директорией. Тогда git worktree list читается с одного взгляда, а сборочные скрипты находят нужное дерево по предсказуемому пути.

Частые ошибки

  • Попытка выложить занятую ветку. «fatal: 'main' is already checked out» — ветка уже в другом дереве. Сделайте новую ветку через -b или используйте --detach.
  • Удаление папки через rm вместо git worktree remove. Останутся висячие ссылки; лечится git worktree prune, но лучше удалять штатно.
  • Ожидание изолированных коммитов. Все деревья делят историю и stash. Коммит, сделанный в одном дереве, моментально виден в git log другого — это фича, а не баг.
  • Пути внутри основного дерева. Не создавайте worktree внутри рабочей папки проекта — кладите рядом (../proj-x), иначе Git и сборщики запутаются.
  • Забытые деревья на CI. Долгоживущие сборочные worktree копятся; периодически git worktree list и уборка, иначе диск засоряется.

Итоги

  • git worktree add <путь> <ветка> создаёт вторую рабочую папку; -b заодно заводит новую ветку.
  • Все деревья делят базу объектов в одном .git/ — это дёшево по диску и держит ветки синхронными.
  • Одну ветку нельзя выложить в два дерева; для разовых задач есть --detach.
  • git worktree list показывает все деревья, remove и prune убирают их.
  • Worktree заменяет связку «stash + переключение» и второй clone при параллельной работе над ветками.
Проверьте себя
1. В чём главное преимущество git worktree перед вторым `git clone` для параллельной работы?
AWorktree автоматически мёржит ветки между собой
BВсе деревья делят одну базу объектов в .git — экономия места и синхронные ветки/история
CWorktree работает без интернета, а clone нет
DWorktree шифрует рабочие файлы
2. Почему `git worktree add ../x main` может вернуть ошибку 'main is already checked out'?
AОдну и ту же ветку нельзя выложить сразу в два рабочих дерева
BПапка ../x не существует
CУ ветки main нет коммитов
DНужны права администратора
3. Папку worktree удалили вручную через rm -rf. Какая команда уберёт оставшиеся висячие ссылки?
Agit worktree clean
Bgit gc --aggressive
Cgit worktree prune
Dgit reset --hard