Git Worktrees: что это, когда и зачем их использовать
Git worktree: чем они отличаются от stash и вторых клонов, когда их использовать и какие команды нужны для управления ими.
Git worktree — это дополнительный рабочий каталог, привязанный к тому же репозиторию, со своей собственной извлечённой веткой. Благодаря этому вы можете держать две ветки открытыми в двух папках одновременно, ничего не клонируя.
Большинство разработчиков сталкиваются с необходимостью в worktree в момент раздражения: stash применился не к той ветке, языковой сервер мучительно переиндексирует проект после checkout, или второй клон того же репозитория никак не удаётся держать в актуальном состоянии. Worktrees решают все три проблемы, причём они поставляются вместе с Git 2.5 с июля 2015 года, так что устанавливать ничего не нужно.
В этой статье разбираемся, чем worktree на самом деле является на диске, какие три команды управляют этой функциональностью, когда worktree выигрывает у stash, когда — у второго клона, и во что он вам обойдётся.
Ключевые выводы
- Git worktree — это второй рабочий каталог, привязанный к тому же репозиторию, со своей извлечённой веткой; связанный каталог содержит небольшой файл
.git, указывающий обратно на основной репозиторий, а не полноценную папку.git. - Одна команда
git worktree add -b hotfix-bug ../hotfix-workspace mainзаменяет всю последовательность из stash, checkout, создания ветки, исправления и stash-pop. - Все worktrees используют одну базу объектов и один набор ссылок, поэтому единственный
git fetchобновляет каждый worktree, и история не дублируется на диске. - Git отслеживает закоммиченные файлы, а не ваше окружение: установленные зависимости, файлы
.envи кеши сборки остаются в исходном каталоге. - По умолчанию Git не позволяет извлечь одну и ту же ветку в двух worktrees; создайте worktree с отсоединённым HEAD с помощью
git worktree add -d, если вам нужно только собрать или протестировать коммит.
Что такое Git worktree на диске?
Связанный worktree — это каталог с извлечёнными файлами, а не копия вашего репозитория. Его .git верхнего уровня — это обычный файл, а не каталог, и он указывает на небольшую папку с метаданными внутри .git/worktrees/ основного репозитория; полностью эта схема описана в документации по git worktree. Всё «тяжёлое» остаётся в одном месте: одна база объектов, одна история и один набор ссылок обслуживают каждый worktree, и лишь несколько файлов принадлежат конкретному worktree — среди них HEAD и индекс.
Этот единственный факт объясняет всё остальное про worktrees. Создание worktree стоит одного checkout, а не clone. Коммиты, сделанные в любом worktree, немедленно видны из всех остальных, потому что под ними лежит один-единственный репозиторий.
Какой рабочий процесс заменяет worktree?
Без worktrees срочное вмешательство означает необходимость приостановить текущее состояние. Знакомая последовательность, с использованием формы git stash push, чтобы сообщение действительно прикрепилось:
git stash push -m "wip: login form"
git checkout main
git checkout -b hotfix-bug
# fix, commit, push, wait for the merge
git checkout feature-login
git stash pop
Пять команд, два переключения контекста и один шанс применить stash не к той ветке. Вариант с worktree — одна команда:
git worktree add -b hotfix-bug ../hotfix-workspace main
Она создаёт соседнюю папку, создаёт ветку hotfix-bug, отходящую от main, и извлекает её туда. Ваш исходный каталог остаётся нетронутым: та же ветка, те же изменённые файлы, тот же открытый редактор. Когда исправление будет смержено, команда git worktree remove ../hotfix-workspace удалит папку и снимет её с регистрации.
Три команды: add, list, remove
Повседневная работа с worktrees сводится к трём подкомандам. git worktree add <path> <branch> извлекает существующую ветку по новому пути; добавьте -b <new-branch> перед путём, чтобы попутно создать ветку. Укажите в add путь, которого ещё не существует, и Git создаст каталог за вас.
git worktree list показывает каждый worktree, начиная с основного, с его путём, сокращённым хешем коммита и извлечённой веткой в квадратных скобках:
/home/you/project abc1234 [feature-login]
/home/you/hotfix-workspace def5678 [hotfix-bug]
git worktree remove <path> удаляет worktree, и правила удаления в Git строги: один неотслеживаемый файл или один изменённый отслеживаемый файл остановят команду, если вы не добавите --force, который эти изменения выбросит. Основной worktree Git не отпустит вообще, а для заблокированного потребуется --force дважды. Если же вы удалите папку worktree вручную, git worktree prune подчистит оставшиеся после неё метаданные.
Когда worktree выигрывает у stash?
Stash приостанавливает вашу работу; worktree позволяет ей продолжаться. Это различие и определяет выбор. Stash хорош, чтобы отложить дифф в две строки на десять минут. Он не подходит, когда работа в вашем каталоге всё ещё «в полёте»: запущенный набор тестов или долгая сборка, dev-сервер, следящий за файлами, или редактор, языковой сервер которого переиндексировал бы весь проект после переключения ветки.
С worktree ничто из этого состояния не нарушается, потому что вы вообще не трогаете исходный каталог. Вы открываете новую папку во втором окне редактора, выполняете там прерывающую задачу и возвращаетесь к тому, что оставили ровно в том же виде. Та же изоляция объясняет, почему инструменты, запускающие AI-агентов для написания кода параллельно, взяли worktrees за основу своей модели работы. Более субъективный взгляд предлагает заметка matklad: в ней описан фиксированный набор из пяти worktrees одного разработчика, сопоставленных с параллельными видами деятельности, а не с ветками; воспринимайте это как личную настройку, а не как общепринятую практику.
Worktree или второй клон?
Worktree выигрывает у второго клона всегда, когда оба каталога должны отслеживать один и тот же репозиторий. Поскольку каждый worktree использует одно хранилище объектов и один набор ссылок, единственный git fetch обновляет их все, ветка, отправленная из одного, мгновенно видна в остальных, а добавление worktree не дублирует историю на диске. Второй клон не даёт ничего из этого: два хранилища объектов, в которые нужно делать fetch, два набора ссылок, расходящихся между собой, и удвоенный расход диска.
Клон всё же остаётся правильным выбором в двух случаях: когда работа относится к действительно другому remote — например, к форку, с которым вы взаимодействуете отдельно, — или когда вы хотите провести одноразовый эксперимент, полностью изолированный от основного репозитория, где даже общие ссылки нежелательны.
| Stash | Worktree | Второй клон | |
|---|---|---|---|
| Работа продолжается | Нет | Да | Да |
| Стоимость настройки | Мгновенно | Один checkout | Полный clone |
| История на диске | Общая | Общая | Дублируется |
| Один fetch обновляет | Да | Да | Нет |
Во что обходятся worktrees?
Git отслеживает закоммиченные файлы, а не ваше окружение. Это основной «налог» на каждый новый worktree:
- Установленные зависимости не переносятся. Свежему worktree проекта на npm или pip обычно нужен собственный шаг установки, прежде чем он соберётся.
- Неотслеживаемые локальные файлы остаются на месте: файлы
.env, локальная конфигурация и кеши сборки живут в исходном каталоге, потому что у Git нет о них никаких записей. - Папки worktree, созданные внутри репозитория, отображаются как неотслеживаемый мусор и требуют записи в
.gitignore. Соседние каталоги (../hotfix-workspace) полностью снимают эту проблему. - По умолчанию одна ветка может быть извлечена только в одном worktree одновременно. Обходные пути существуют (
--forceдляadd,--ignore-other-worktreesдляswitch), но более чистое решение, когда вам нужно лишь собрать или протестировать коммит, — worktree с отсоединённым HEAD:
git worktree add -d ../build-check 1a2b3c4
Флаг -d оставляет HEAD в новой папке отсоединённым, поэтому ни одна ветка не занимается и ограничение никогда не срабатывает. Команда git switch -d <commit> внутри существующего worktree делает то же самое.
Достаточно ли у вас параллельных задач?
Тест на целесообразность короток. Регулярно ли вас прерывают, когда у вас есть реальная незакоммиченная работа? Приводит ли переключение ветки к переустановке зависимостей или переиндексации языковым сервером, которую вы явно ощущаете? Поддерживаете ли вы уже второй клон того же репозитория? Два «да» — и worktrees окупятся за первую же неделю. Ноль — и stash вместе с ветками вполне достаточно. Начните с одного: в следующий раз, когда срочное исправление свалится посреди работы над фичей, выполните git worktree add вместо git stash, а папку удалите, когда исправление будет смержено.
Часто задаваемые вопросы
Работают ли git worktrees с репозиториями, использующими сабмодули?
Частично. В собственном руководстве Git поддержка сабмодулей помечена как незавершённая в разделе BUGS, и там же не рекомендуется извлекать суперпроект более чем в одном месте одновременно. На практике связанный worktree с сабмодулями внутри нельзя переместить с помощью git worktree move, а для его удаления через git worktree remove требуется флаг force. Если ваш репозиторий сильно опирается на сабмодули, второй клон будет более безопасным выбором.
Общие ли stash и ветки между git worktrees?
Да. Всё, что лежит под refs/, является общим для каждого worktree, поэтому ветки, теги и stash выглядят одинаково, где бы вы ни находились. Исключение составляют три пространства имён, остающиеся локальными для одного worktree: refs/bisect, refs/worktree и refs/rewritten. Помимо них, приватными являются только собственные указатели worktree, среди которых HEAD и его индекс. Таким образом, stash, созданный в одном worktree, можно увидеть в списке и применить из любого другого worktree того же репозитория.
Можно ли переместить worktree в другую папку после создания?
Да. Передайте git worktree move сам worktree и путь, по которому вы хотите его видеть, и Git за один раз переместит папку и перепишет её метаданные. Два случая, с которыми команда не справится: основной worktree и любой связанный worktree с сабмодулями внутри. Для заблокированного worktree потребуется флаг force дважды. Если вы уже сами перетащили папку в другое место, git worktree repair восстановит связь.
Что произойдёт, если запустить git worktree add только с путём, без ветки?
Git возьмёт последнюю часть пути как имя ветки, создаст эту ветку от HEAD и извлечёт её в новую папку. Так, git worktree add ../hotfix оставит вас с веткой hotfix, извлечённой в ../hotfix. Если такое имя уже занято, Git вместо этого извлечёт существующую ветку — при условии, что её сейчас не удерживает другой worktree.