如果 Git 报告你的 Mac 上的某个文件变为了可执行文件——或不再是可执行文件——请首先找出哪一层发生了变化:工作区文件、暂存区索引还是某个提交。在更改权限或 Git 配置之前,先对比这些状态。Git 将常规文件记录为模式 100644(不可执行)或 100755(可执行);两者之间孤立的变更通常指向可执行权限位,而非文件内容的更改。Git diff 文档
1. 检查更改是否已暂存
在终端中,进入代码仓库并检查其状态以及两个层级的 diff:
git status --short
git diff --summary
git diff --cached --summary
git diff 比较工作区与暂存区索引;git diff --cached 比较暂存区索引与 HEAD。Git status 会区分这些相同的层级,因此已暂存的更改会与未暂存的更改分开显示。Git status 文档
寻找类似于 mode change 100644 => 100755 path/to/file 的输出。如果它只出现在 git diff --summary 中,则说明工作区文件与暂存区索引不同。如果它出现在 git diff --cached --summary 中,则表示模式变更已被暂存,可以进行提交。如果两者都为空,但有队友报告了已提交的变更,接下来请检查历史记录。
2. 比较索引与 Mac 本地文件
对于已跟踪的路径,检查索引中记录的模式和文件的当前权限:
git ls-files --stage -- "scripts/build.sh"
ls -l "scripts/build.sh"
将 scripts/build.sh 替换为相对于仓库的准确路径;用引号括起来,以便空格和 shell 字符能被按字面处理。在 git ls-files --stage 输出中,第一个字段是存储在索引中的模式:100644 表示 Git 未将该文件记录为可执行文件,而 100755 则表示记录为可执行文件。ls -l 输出显示本地权限位;权限三元组中的 x 表示该类用户具有执行权限。Git ls-files 文档和 Apple 权限指南解释了这两种视图。
这种对比有助于缩小原因范围。如果索引显示 100644,但 ls -l 显示执行权限,则差异存在于工作区文件中(或者 Git 被配置为忽略文件模式差异)。如果索引显示 100755,无论你原本希望该文件是什么状态,Git 都已经暂存或检出了可执行模式。
3. 找到引入该模式的提交
如果该更改已经存在于历史记录中,请检查相关文件的提交摘要:
git log --summary -- "scripts/build.sh"
当文件模式发生变化时,Git 的 patch 格式会分别打印 old mode 和 new mode 标头。查看这些值发生切换的提交,然后对比其父提交及周围的工作流:该文件是特意被设置为可执行的吗?是某个编辑器或脚本更改了权限吗?还是该仓库曾被用在其他文件系统或操作系统上?该提交指明了 Git 是在何时记录下这一更改的;它无法证明具体是哪个应用程序或哪个人导致的。Git diff 格式文档
跨越多个无关文件的模式变更可能表明存在大范围的权限设置操作,或者发生了文件系统/环境迁移。Git 文档指出,某些文件系统无法保留可执行权限位,并且在不同环境之间迁移的仓库可能会遇到不同的文件模式行为。请将此作为调查线索,而非特定原因的确凿证据。Git 配置文档
4. 检查 Git 的文件模式设置
检查该设置及其来源:
git config --show-origin --get core.filemode
core.filemode 控制 Git 是否遵循工作区中的可执行权限位差异。未指定时,Git 默认将其设为 true,并可能在仓库初始化或克隆时探测文件系统行为。如果该命令输出 false,Git 可能会忽略本地的可执行权限位更改;该设置无法解释已记录在提交中的模式。如果没有任何输出,则该设置可能未设定,此时适用 Git 文档中记载的默认值。Git 配置文档
切忌仅仅为了隐藏某个令人意外的 diff 就将此选项设为 false。首先应确认仓库是否位于无法可靠保留可执行权限位的文件系统或共享环境上,以及协作者是否依赖模式跟踪。更改此设置只会改变 Git 处理模式差异的方式;它并不能修复索引或提交中已错误记录的模式。
5. 仅在明确预期模式后再进行修复
如果文件不应是可执行文件且更改尚未暂存,请使用 chmod 恢复本地权限:
chmod a-x "scripts/build.sh"
git diff --summary
如果预期的模式是可执行,请改用 chmod a+x "scripts/build.sh"。在 macOS 上,chmod 会更改指定项的权限;请根据文件的作用和仓库规范来选择预期的模式,而不仅仅是为了消除 diff。Apple 权限指南
如果已暂存了错误的模式,请先设置预期的本地权限,然后再次暂存该路径并查看暂存摘要:
chmod a-x "scripts/build.sh"
git add -- "scripts/build.sh"
git diff --cached --summary
当文件应为可执行时,用 a+x 代替 a-x。如果已有提交中的模式错误,通常创建一个新的修正提交比重写共享历史记录更为清晰明确;在重写他人可能已在使用的提交之前,请先进行协调。在任何情况下,事后都应检查 git diff 和 git diff --cached,以免在修改权限的同时忽略了内容编辑。
参考来源
Git - git-diff 文档 — Git
Git - git-status 文档 — Git
Git - git-ls-files 文档 — Git
Git - git-config 文档 — Git
BSD 权限与所有权 — Apple Developer