GitWorktree.org logoGitWorktree.org

Git Worktree with Xcode

Xcode projects have unique challenges when working with git worktree: scheme files, derived data, and the notorious .xcodeproj merge conflicts. This guide covers how to set up and manage worktrees effectively for iOS, macOS, and Swift development in Xcode.

Xcode and Git Worktree Compatibility

Xcode works well with git worktrees out of the box. Each worktree is a standard directory containing your full project, so you can open the .xcodeproj or .xcworkspace from any worktree just as you would from a normal clone. Xcode recognizes the git repository and shows branch information, diffs, and blame annotations correctly.

The main benefit for iOS developers is the ability to keep a stable branch open in one Xcode window while working on a feature or bug fix in another. No more stashing changes or waiting for Xcode to re-index after switching branches. Each worktree maintains its own build state, so you can build and run both branches simultaneously on different simulators.

Open a worktree in Xcode
# Create a worktree for a feature branch
git worktree add ../MyApp-feature feat/new-onboarding

# Open the worktree project in Xcode
open ../MyApp-feature/MyApp.xcodeproj

Setting Up Worktrees for iOS Projects

When creating worktrees for an Xcode project, place them as sibling directories to your main checkout. This keeps paths predictable and avoids confusing Xcode with nested project structures.

Create worktrees for an iOS project
# From your main project directory
cd ~/Developer/MyApp

# Create worktrees as siblings
git worktree add ../MyApp-bugfix fix/crash-on-launch
git worktree add ../MyApp-experiment experiment/swiftui-navigation

# List all active worktrees
git worktree list
# /Users/you/Developer/MyApp                   abc1234 [main]
# /Users/you/Developer/MyApp-bugfix            def5678 [fix/crash-on-launch]
# /Users/you/Developer/MyApp-experiment         ghi9012 [experiment/swiftui-navigation]

If your project uses CocoaPods, you will need to run pod install in each new worktree since the Pods directory is typically gitignored. For Swift Package Manager dependencies, Xcode resolves them automatically when you open the project.

Set up CocoaPods in a worktree
# After creating a worktree with CocoaPods
cd ../MyApp-bugfix
pod install

# Then open the workspace (not the project)
open MyApp.xcworkspace

Managing Xcode Project Files Across Worktrees

The project.pbxproj file is the most conflict-prone file in any Xcode project. Git worktrees help reduce these conflicts because each worktree works on its own branch independently. However, when two worktrees both add or remove files, you will still encounter merge conflicts when bringing branches together.

To minimize issues, follow these practices:

  • Add new files in only one worktree at a time when possible. Merge frequently to keep branches in sync.
  • Use Xcode's folder references instead of groups for directories that change often, as they generate fewer pbxproj entries.
  • Consider using xcodeproj merge drivers or tools like mergepbx to handle project file merges automatically.
Set up mergepbx for cleaner project file merges
# Install mergepbx for better Xcode merge handling
brew install mergepbx

# Add to your .gitattributes
echo "*.pbxproj merge=mergepbx" >> .gitattributes

# Configure the merge driver
git config merge.mergepbx.name "Xcode project file merge driver"
git config merge.mergepbx.driver "mergepbx %O %A %B"

Derived Data and Build Folders

By default, Xcode stores derived data in ~/Library/Developer/Xcode/DerivedData/. Each worktree project gets its own derived data subfolder based on the project name and path hash, so builds from different worktrees do not interfere with each other.

If you want even clearer separation or need to manage disk space, you can configure Xcode to use a per-worktree build directory:

Manage derived data across worktrees
# Option 1: Set derived data location per workspace in Xcode
# Xcode > Settings > Locations > Derived Data > Relative

# Option 2: Use xcodebuild with a custom derivedDataPath
xcodebuild -scheme MyApp \
  -derivedDataPath ./build/DerivedData \
  -destination 'platform=iOS Simulator,name=iPhone 16'

# Option 3: Clean derived data for a specific worktree
rm -rf ~/Library/Developer/Xcode/DerivedData/MyApp-*

Keep in mind that derived data can consume significant disk space. When you remove a worktree, the associated derived data in ~/Library/Developer/Xcode/DerivedData/ is not automatically cleaned up. Periodically delete old derived data folders to reclaim disk space.

Tips for Swift/SwiftUI Projects

Run Multiple Simulators

With worktrees, you can run your app on different simulators from different branches at the same time. Open each worktree's project in a separate Xcode window, select different simulator destinations, and hit Run in both. This is especially useful for comparing UI changes or testing a fix against the current release.

SwiftUI Previews Work Per-Worktree

SwiftUI previews are tied to the project context, so each worktree gets its own preview state. You can have previews running in your main worktree showing the stable UI while iterating on a redesign in another worktree, all without any preview conflicts or cache issues.

Swift Package Manager Dependencies

SPM packages are resolved per workspace. When you open a worktree in Xcode, it will automatically resolve packages based on the Package.resolved file in that branch. If branches have different dependency versions, each worktree correctly uses the right versions independently.

Signing and Provisioning

Code signing settings are stored in the .xcodeproj and are shared across worktrees through git. If you use automatic signing, each worktree will work without additional configuration. For manual signing, ensure your provisioning profiles are installed system-wide so they are available regardless of which worktree you are building from.

Git worktrees integrate smoothly with Xcode and are particularly valuable for iOS development where branch switching can trigger lengthy re-indexing and rebuild cycles. For more on managing worktrees, see the git worktree add tutorial or return to the IDE integration overview.