好幾年前我曾經寫過一篇讓 Xcode 專案易於版本控制的方法,它非常的好用,我也在多個專案裡頭使用這樣的配置用了好多年,都沒有出過什麼問題。

直到最近遇到了 SwiftUI 的 Preview 功能。

猜測可能是因為原先的設定是在 Build phase 去執行排序的 script,導致它破壞了 SwiftUI Preview 的一些機制,我想到了幾個解法,像是

  • 不要放在 Build phase,放到 Run phase 去執行。
  • 改用 XcodeGen 之類的工具產生 project file。
  • 使用 git hook 的 pre-commit 來執行排序。
  • 不要用,等真的遇到 merge conflict 了再說 lol

最後我選擇了 pre-commit 的做法。

如同在讓 Xcode 專案易於版本控制的方法這篇提到的,我的腳本都放在專案根目錄底下的 Scripts 目錄裡頭。

1.

首先建立一個 pre-commit.sh 放在 Scripts 目錄裡,它會檢查修改的檔案有沒有 project.pbxproj 檔,有的話就排序它然後把它加回 git 版控,這樣我們就不用再手動加一次。檔案內容如下:

#!/bin/sh
#
# Following script is to sort Xcode project files, and add them back to version control.
# The reason to sort project file is that it can reduce project.pbxproj file merging conflict possibility.
#
echo 'Sorting Xcode project files'

GIT_ROOT=$(git rev-parse --show-toplevel)
script_name="$GIT_ROOT/Scripts/sort-Xcode-project-file"
modifiedProjectFiles=( $(git diff --name-only --cached | grep "project.pbxproj") )

for filePath in ${modifiedProjectFiles[@]}; do
  fullFilePath="$GIT_ROOT/$filePath"
  perl $script_name $fullFilePath
  git add $fullFilePath
done

echo 'Done sorting Xcode project files'

exit 0

2.

我把這個檔案放在 Scripts 目錄裡而不是直接去修改 .git/hooks/pre-commit 檔的原因是因為這樣才可以把它加入版本控管,讓團隊其他成員也可以用。所以現在我們需要一個方法讓 pre-commit 執行這個檔案,我在 Scripts 目錄底下新增了一個 install-git-hooks.shchmod +x 讓它可以被執行,內容如下:

#!/bin/bash

GIT_ROOT=$(git rev-parse --show-toplevel)
HOOKS_PATH=$(git rev-parse --git-dir)/hooks

if [ ! -d "$HOOKS_PATH" ]; then
  mkdir "$HOOKS_PATH"
fi

ln -s -f "$GIT_ROOT/Scripts/pre-commit.sh" "$HOOKS_PATH/pre-commit"
chmod +x "$HOOKS_PATH/pre-commit"

echo "git hooks installed!"

這樣就可以建立 symbolic link 了。因為目前只有一個 pre-commit.sh,所以這個安裝腳本寫得很簡單,日後如果要安裝的 hook 變多了再來調整就好。

參考資料