在 iOS 项目里记录构建时对应的 Git 提交信息,常见用途包括问题追踪、灰度排查和测试包比对。比较直接的做法,是在 Xcode 构建阶段把当前分支名和提交哈希写进 Info.plist,再由应用运行时读取;如果过程中遇到 Operation not permitted,通常还需要一并检查 User Script Sandboxing 的限制。
下面按“为什么会报权限错误、Info.plist 需要准备什么、脚本怎么写、代码如何读取”几个问题展开,方便你直接照着配置,也能判断这种方案在哪些场景下适合使用。
为什么脚本会遇到权限错误
User Script Sandboxing 用来控制 Xcode 构建脚本的沙盒限制。默认情况下,Xcode 会把脚本放进沙盒环境,对文件系统访问进行约束;当脚本需要读取或修改某些目录内容时,尤其是项目目录之外的文件,比较容易遇到权限问题。

如果构建脚本报出 Operation not permitted,可以先检查这一项设置。将 User Script Sandboxing 设置为 NO 后,脚本可以访问文件系统中的更多位置,从而避免这类错误。
如何关闭 User Script Sandboxing
- 在项目的 TARGETS 中打开 Build Settings。
- 搜索 User Script Sandboxing。
- 将它设置为 NO。
完成后,构建脚本在执行期间会拥有更宽松的文件访问能力,更适合需要读写 Info.plist 或其他构建产物的场景。
关闭沙盒前要注意什么
- 安全性:禁用沙盒后,脚本拥有更高的文件系统访问权限。如果脚本里包含不受信任的操作,构建过程的风险也会随之增加。
- 系统权限:即便关闭了沙盒,系统级权限仍然可能影响文件读写。比如涉及
/etc/或/System/这类目录时,依然可能需要管理员权限。
因此,这个设置适合用于你能够确认脚本来源、且确实需要额外访问权限的项目环境。
先在 Info.plist 里预留 Git 字段
要把 Git 信息带进应用,第一步是确保 Info.plist 里有对应字段。你可以手动先加好键值,也可以配合构建脚本动态更新。
文中的字段包括两个:
GitCommitHashGitBranch
对应内容如下:
<key>GitCommitHash</key>
<string>your_git_commit_hash_here</string>
<key>GitBranch</key>
<string>your_git_branch_here</string>
其中,your_git_commit_hash_here 和 your_git_branch_here 只是占位内容,后续会在构建阶段被脚本替换成当前仓库的真实值。
在构建阶段把 Git 信息写入 Info.plist
准备好字段后,就可以在 Xcode 的构建流程中加入自定义脚本。这个脚本会读取当前 Git 仓库状态,拿到短提交哈希和当前分支名,再写回 Info.plist。

在 Xcode 中添加 Run Script Phase
- 在 Xcode 中选择对应项目目标。
- 进入 Build Phases。
- 点击左上角
+,选择 New Run Script Phase。 - 把下面的脚本加入脚本输入框。
#!/bin/bash
# 获取当前 Git 提交的哈希值(短格式)
commit_hash=$(git rev-parse --short HEAD)
# 获取当前 Git 分支的名称
branch_name=$(git rev-parse --abbrev-ref HEAD)
# 获取 Info.plist 文件路径
plist_path="${PROJECT_DIR}/YourProject/Info.plist" # 请替换为实际路径
# 修改 Info.plist 中的 Git 信息
/usr/libexec/PlistBuddy -c "Set :GitCommitHash $commit_hash" "$plist_path"
/usr/libexec/PlistBuddy -c "Set :GitBranch $branch_name" "$plist_path"
保存后重新构建,脚本会在每次构建时执行,并把当前的 Git 提交哈希和分支名称写入 GitCommitHash、GitBranch 这两个键。
这个脚本实际做了什么
- 通过
git rev-parse --short HEAD获取当前提交的短哈希。 - 通过
git rev-parse --abbrev-ref HEAD获取当前分支名称。 - 通过
/usr/libexec/PlistBuddy修改指定Info.plist中的键值。
要注意的是,脚本中的 ${PROJECT_DIR}/YourProject/Info.plist 只是示例路径,必须替换成项目里的实际文件位置,否则写入不会生效。
应用运行时如何读取 Git 信息
写入完成后,应用侧就可以直接从 Bundle.main.infoDictionary 读取这些值。原文给出的 Swift 示例如下:
// MARK: - Git 相关信息
/// 获取 Git 提交版本号
static var gitCommitHash: String {
guard let commitHash = Bundle.main.infoDictionary?["GitCommitHash"] as? String else {
return "未知提交"
}
return commitHash
}
/// 获取 Git 分支名称
static var gitBranch: String {
guard let branch = Bundle.main.infoDictionary?["GitBranch"] as? String else {
return "未知分支"
}
return branch
}
这种读取方式的好处是实现简单,不需要额外依赖;只要构建阶段已经把值写进包内配置,运行时就能直接取到。对于调试页、关于页面、崩溃日志附带版本信息等场景,这种做法都比较实用。
适用场景与配置要点
整套做法可以概括为三步:
- 先在
Info.plist中预留GitCommitHash和GitBranch键。 - 在 Build Phases 中添加脚本,利用 Git 和
PlistBuddy在构建时写入实际值。 - 在代码里通过
Bundle.main.infoDictionary读取并展示。
如果构建阶段报权限错误,再回头检查 User Script Sandboxing 是否需要设为 NO。这能解决不少脚本访问受限的问题,但同时也意味着你需要更谨慎地管理构建脚本的安全边界。













