Пока релизная ветка проходит регрессионное тестирование, проблема в продакшене может потребовать срочно выпустить исправление на основе старого коммита, а функциональную ветку при этом нужно продолжать собирать. Если постоянно выполнять git switch в одном каталоге, непроиндексированные файлы, состояние сценариев сборки и промежуточные артефакты Xcode легко начинают влиять на соседние задачи. Надёжнее хранить одну базу объектов Git на выделенном облачном Mac и с помощью Git Worktree создавать отдельный рабочий каталог для каждой задачи.
Worktree изолирует состояние, а не просто ускоряет переключение веток
Обычно для получения нескольких рабочих каталогов репозиторий клонируют несколько раз. В крупном проекте это приводит к дублированию объектов Git, увеличивает время загрузки и усложняет обслуживание. Worktree использует общую базу объектов, но каждому каталогу предоставляет собственные извлечённые файлы, индекс и HEAD. Это удобно для параллельной работы над релизами, срочными исправлениями и новыми функциями.
При этом Worktree не изолирует артефакты Xcode автоматически. Если несколько рабочих деревьев по-прежнему записывают данные в стандартный каталог DerivedData, индексы, кэши модулей и промежуточные файлы всё ещё могут перезаписывать друг друга. Поэтому разделять нужно как каталоги с исходным кодом, так и результаты сборки.
| Объект | Изолируется автоматически | Рекомендация |
|---|---|---|
| Отслеживаемые исходные файлы | Да | Используйте отдельную ветку для каждой задачи |
| Неотслеживаемые файлы | Да | Не храните учётные данные в каталоге репозитория |
| Объекты Git | Нет | Совместное использование сокращает расход дискового пространства |
| DerivedData | Нет | Назначайте отдельный путь для каждого рабочего дерева |
| Журналы и пакеты результатов | Нет | Используйте каталоги с идентификатором ветки |
Ценность Worktree не в ускорении отдельной сборки, а в уменьшении скрытого взаимного влияния параллельных задач.
Сначала определите структуру каталогов, затем создавайте рабочие деревья
Основной репозиторий, рабочие деревья и артефакты сборки рекомендуется хранить в трёх соседних областях. Благодаря такому разделению при удалении рабочего дерева не будут случайно удалены результаты тестирования, которые ещё требуется архивировать.
~/projects/mobile-app
~/worktrees/release-3.4
~/worktrees/hotfix-3.4.1
~/build-data/release-3.4
~/build-data/hotfix-3.4.1
Сначала обновите ссылки на удалённый репозиторий в основном каталоге, а затем создайте рабочие деревья:
cd "$HOME/projects/mobile-app"
git fetch --prune
mkdir -p "$HOME/worktrees" "$HOME/build-data"
git worktree add "$HOME/worktrees/release-3.4" release/3.4
git worktree add -b hotfix/3.4.1 \
"$HOME/worktrees/hotfix-3.4.1" origin/release/3.4
git worktree list
По умолчанию git worktree add не позволяет одновременно использовать одну ветку в двух рабочих деревьях. Это защитный механизм, который не следует обходить принудительными параметрами. Если требуется только проверить определённый исторический коммит, создайте рабочее дерево в состоянии detached:
git worktree add --detach "$HOME/worktrees/audit-build" 8f32c1a
Создайте стабильное соответствие между ветками и каталогами
Имена веток часто содержат /, поэтому их нельзя напрямую использовать как имена одноуровневых каталогов. Сценарий автоматизации должен заменять косые черты и пробелы дефисами, сохраняя исходное имя ветки для операций Git. Он также должен отклонять пустые аргументы, чтобы случайно не записать данные в корневой или общий каталог.
Назначьте каждой задаче Xcode отдельный путь вывода
После перехода в рабочее дерево задайте каталог промежуточных артефактов с помощью -derivedDataPath, а для сохранения диагностических данных тестирования или сборки используйте -resultBundlePath. К началу выполнения путь пакета результатов не должен существовать, поэтому уникальное имя можно сформировать с помощью временной метки.
set -euo pipefail
BRANCH="${1:?branch required}"
SAFE_NAME="$(printf '%s' "$BRANCH" | tr '/ ' '--')"
WORKTREE="$HOME/worktrees/$SAFE_NAME"
OUTPUT="$HOME/build-data/$SAFE_NAME"
STAMP="$(date '+%Y%m%d-%H%M%S')"
mkdir -p "$OUTPUT/logs" "$OUTPUT/results"
cd "$WORKTREE"
xcodebuild \
-workspace MobileApp.xcworkspace \
-scheme MobileApp \
-configuration Debug \
-derivedDataPath "$OUTPUT/DerivedData" \
-resultBundlePath "$OUTPUT/results/$STAMP.xcresult" \
build 2>&1 | tee "$OUTPUT/logs/$STAMP.log"
Даже в одном рабочем дереве могут одновременно выполняться две задачи. В таком случае разделения только по веткам недостаточно: добавьте идентификатор задачи, например release-3.4/job-17/DerivedData. Для задач архивирования также следует отдельно указывать путь архива, чтобы более поздний запуск не перезаписал предыдущий результат.
Не используйте общий кэш модулей без необходимости
Общий кэш кажется удобным способом экономии места, но если две ветки используют разные параметры компиляции, наборы инструментов или сценарии генерации, ошибочное попадание в кэш диагностировать сложнее, чем выполнить повторную сборку. Сначала создайте стабильную базовую конфигурацию с полной изоляцией. Только убедившись, что версии инструментов, файлы блокировки зависимостей и параметры сборки совпадают, можно рассматривать совместное использование загруженных ресурсов в режиме только для чтения, но не всего каталога DerivedData.
Перед параллельным запуском выполните четыре проверки
Перед первой сборкой в каждом рабочем дереве запишите коммит, состояние рабочей области и фактические параметры сборки:
git rev-parse HEAD
git status --porcelain
xcodebuild -version
xcodebuild \
-workspace MobileApp.xcworkspace \
-scheme MobileApp \
-showBuildSettings > "$OUTPUT/build-settings.txt"
Команда git status --porcelain не должна ничего выводить. Если присутствуют сгенерированные файлы, сначала выясните, следует ли добавить их в список игнорируемых, а не скрывайте проблему командой очистки. Затем одновременно запустите две низкорисковые сборки Debug и убедитесь, что журналы, пакеты результатов и DerivedData сохраняются в соответствующих каталогах.
Как минимум проверьте следующее:
- Хеш коммита, записанный каждой задачей, соответствует целевой ветке.
- Очистка DerivedData одной задачи не влияет на другую.
- Имена пакетов результатов, журналов и архивов не совпадают.
- По журналу сбоя можно определить рабочее дерево, коммит и конфигурацию сборки.
- В системе достаточно свободной памяти и места на диске для параллельных задач.
Безопасно удаляйте рабочие деревья и артефакты сборки
Перед удалением перейдите в целевое рабочее дерево и выполните git status --short, а также убедитесь, что его не использует активный процесс xcodebuild. Журналы, пакеты результатов и архивы, которые требуется сохранить, следует перенести из временного каталога. Затем выполните следующие команды из основного репозитория:
cd "$HOME/projects/mobile-app"
git worktree remove "$HOME/worktrees/hotfix-3.4.1"
git worktree prune
git worktree list
Если каталог содержит незафиксированные изменения, git worktree remove откажется его удалять. Не используйте --force как стандартный способ очистки. Сначала зафиксируйте изменения, сохраните их в явно обозначенном месте или вручную подтвердите удаление. Артефакты сборки также следует удалять по каталогам задач, избегая подстановочных символов с неочевидной областью действия.
При длительной эксплуатации можно добавить реестр рабочих деревьев в систему управления задачами и записывать ветку, ответственного, время создания, каталог вывода и ожидаемое время освобождения. Это не позволит заброшенным каталогам бесконтрольно занимать место на SSD и одновременно защитит используемые окружения для срочных исправлений от случайного удаления.
Часто задаваемые вопросы
Можно ли открыть одну ветку Git сразу в двух Worktree?
По умолчанию нельзя. Для параллельных изменений нужны разные ветки, а для просмотра без изменений можно создать Worktree в detached-режиме.
Можно ли использовать общий DerivedData для нескольких Worktree?
Не следует. Параллельные сборки способны перезаписать индексы, кеши модулей и промежуточные файлы, поэтому пути необходимо разделять.
Что проверить перед удалением Worktree?
Убедитесь, что нет несохранённых изменений и работающих процессов xcodebuild, затем сохраните нужные архивы, результаты тестов и журналы.
Сохраняйте воспроизводимую среду на одном облачном Mac
Выберите фиксированные модель, регион и срок аренды, а затем используйте полноценный графический интерфейс macOS и командную строку для разработки или автоматизации.