Git Worktreeで並行Xcodeブランチを安全に分離する

CI/CD ·約 11 分

Git Worktreeで並行Xcodeブランチを安全に分離する

リリースブランチで回帰テストを進めている最中に、本番環境の問題に対応するため、過去のコミットから緊急修正ブランチを切る必要が生じることがあります。その間も、機能ブランチのビルドは継続しなければなりません。同じディレクトリで git switch を繰り返すと、未追跡ファイルやビルドスクリプトの状態、Xcode の中間生成物が混在しやすくなります。より安全なのは、専有クラウド Mac 上に Git オブジェクトストアを1つだけ保持し、Git Worktree を使ってタスクごとに独立した作業ディレクトリを用意する方法です。

Worktreeの目的はブランチ切り替えの高速化ではなく、状態の分離

複数ディレクトリで作業する一般的な方法は、リポジトリを何度もクローンすることです。コードベースが大きい場合、Git オブジェクトが重複してディスクを消費し、ダウンロード時間と保守コストも増加します。Worktree は基盤となるオブジェクトストアを共有しながら、ディレクトリごとに独立したチェックアウト内容、インデックス、HEAD を持つため、リリース、緊急修正、機能開発を並行して進める用途に適しています。

ただし、Xcode の生成物まで自動的に分離されるわけではありません。複数のワークツリーがデフォルトの DerivedData に書き込むと、インデックス、モジュールキャッシュ、中間ファイルが引き続き上書きされる可能性があります。そのため、ソースディレクトリとビルド出力の両方を分離する必要があります。

項目 自動的に分離されるか 推奨事項
追跡対象のソースコード はい タスクごとに独立したブランチを使用する
未追跡ファイル はい 認証情報をリポジトリディレクトリに保存しない
Git オブジェクト いいえ 共有してディスク使用量の重複を抑える
DerivedData いいえ ワークツリーごとに独立したパスを指定する
ログと結果バンドル いいえ ブランチ識別子を含むディレクトリを使用する

Worktree の価値は、1回のビルドを高速化することではなく、並行タスク間で見えにくい状態汚染を減らすことにあります。

ワークツリーを作成する前にディレクトリ規則を固定する

メインリポジトリ、ワークツリー、ビルド生成物は、同じ階層にある3つの領域へ分けることを推奨します。分離しておけば、ワークツリーを削除しても、保存が必要なテスト結果を誤って消すことがありません。

~/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"

同じワークツリー内で2つのタスクが同時に実行されることもあります。その場合、ブランチ単位の分離だけでは不十分です。release-3.4/job-17/DerivedData のように、ジョブ番号もパスへ追加してください。アーカイブタスクでは保存先も個別に指定し、後から開始したタスクが先の結果を上書きしないようにします。

モジュールキャッシュを安易に共有しない

キャッシュ共有は一見すると容量の節約になりますが、2つのブランチでコンパイルオプション、ツールチェーン、生成スクリプトが異なる場合、誤った状態がキャッシュヒットすると、再コンパイルよりも原因の特定が難しくなります。まず完全に分離した構成で安定した基準を確立してください。ツールのバージョン、依存関係のロックファイル、ビルドオプションが一致していることを確認してから、DerivedData 全体を直接共有するのではなく、読み取り専用のダウンロードリソースを共有できるか検討します。

並列実行前に4項目を検証する

各ワークツリーを初めてビルドに使用する前に、コミット、作業ディレクトリの状態、実際のビルド設定を記録します。

git rev-parse HEAD
git status --porcelain
xcodebuild -version
xcodebuild \
  -workspace MobileApp.xcworkspace \
  -scheme MobileApp \
  -showBuildSettings > "$OUTPUT/build-settings.txt"

git status --porcelain は何も出力しない状態が正常です。生成ファイルが存在する場合は、無視対象にすべきファイルかどうかを先に確認し、クリーンアップコマンドで問題を隠さないでください。その後、リスクの低い Debug ビルドを2つ同時に開始し、ログ、結果バンドル、DerivedData がそれぞれ専用ディレクトリへ保存されることを確認します。

検証時には、少なくとも次の項目を確認してください。

  1. 2つのタスクで記録されたコミットハッシュが、それぞれの対象ブランチと一致している。
  2. 一方のタスクで DerivedData を削除しても、もう一方のタスクに影響しない。
  3. 結果バンドル、ログ、アーカイブの名前が重複しない。
  4. 失敗ログからワークツリー、コミット、ビルド構成を特定できる。
  5. システムのメモリとディスク空き容量が、並行タスクを処理するのに十分である。

ワークツリーとビルド生成物を安全に削除する

削除前に対象のワークツリーへ移動して 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ブランチを2つのWorktreeで同時に開けますか?

通常は開けません。並行して編集する場合は別ブランチを作成し、参照だけならdetached状態のWorktreeを利用します。

複数のWorktreeでDerivedDataを共有してもよいですか?

推奨しません。同時ビルドでインデックス、モジュールキャッシュ、中間生成物が上書きされるため、個別の保存先を指定します。

Worktreeを削除する前に何を確認しますか?

未コミットの変更と実行中のxcodebuildがないことを確認し、必要なアーカイブ、テスト結果、ログを保存してから削除します。

専用物理ノード

再現可能な環境を同じクラウドMacに保持する

固定のモデル、リージョン、契約期間を選び、完全なmacOSグラフィカルインターフェースとコマンドラインで開発や自動化タスクを実行できます。

クラウドMacを今すぐレンタル