bigywater.tech
Git · 13

文件权限位与 core.fileMode

为什么改了文件权限 git status 也会报告改动?core.fileMode 到底管什么?这篇造十种权限的文件看 git 记成什么、检出后又变成什么,再比较 fileMode 为 true 和 false 时 chmod、add、update-index 的表现。

2 种
十种磁盘权限提交进去只有 100644 和 100755 两种
0 行
只改权限的提交,--stat 里 data.txt | 0,但这个提交仍然存在
静默
core.fileMode=false 时 chmod 644 一个已提交为 100755 的文件,status 不显示任何变化

只记录一个位

实测 · 本机执行输出
  $ git add -A && git ls-files -s | awk '{print $1, $4}' | sort -k2
  100644 data.txt
  100644 p600
  100644 p611
  100644 p640
  100644 p644
  100644 p664
  100755 p700
  100755 p711
  100755 p744
  100755 p755
  100755 p775
  100755 run.sh
  $ stat -f '%Lp %N' p600 p611 p640 p664 p700 p711 p744 p775 | sort -k2
  600 p600
  611 p611
  640 p640
  664 p664
  700 p700
  711 p711
  744 p744
  775 p775

git 对每个文件只记录一个信息:属主有没有可执行位,记成 100755 或 100644。读写位、组和其他人的权限一概不记。

检出时重新生成

实测 · 本机执行输出
  $ git commit -q -m 第一次 && git clone -q . ../l9clone && cd ../l9clone && umask && stat -f "%Lp %N" p600 p611 p640 p664 p700 p711 p744 p775 p644 p755 | sort -k2
  0022
  644 p600
  644 p611
  644 p640
  644 p644
  644 p664
  755 p700
  755 p711
  755 p744
  755 p755
  755 p775

检出时按 umask 重新生成权限:100644 变成 666 & ~umask(这里是 644),100755 变成 777 & ~umask(755)。所以 600、664 这样的细节无法通过 git 传递。

fileMode=true

实测 · 本机执行输出
  $ chmod 755 data.txt; git status --short; git diff
   M data.txt
  diff --git a/data.txt b/data.txt
  old mode 100644
  new mode 100755
  $ git add data.txt; git ls-files -s data.txt; git commit -q -m 改权限; git log -1 --stat --format=%s
  100755 587be6b4c3f93f93c489c0111bba5596147a26cb 0	data.txt
  改权限
  
   data.txt | 0
   1 file changed, 0 insertions(+), 0 deletions(-)

默认情况下 chmod 会被 git 看作改动:git diff 显示 old mode / new mode,可以提交,提交里只有模式变化而没有行数变化。

fileMode=false

实测 · 本机执行输出
  $ git config core.fileMode false; chmod 644 data.txt; git status --short; echo "[fileMode=false 下 chmod 644 后 status 为空]"; git ls-files -s data.txt
  [fileMode=false 下 chmod 644 后 status 为空]
  100755 587be6b4c3f93f93c489c0111bba5596147a26cb 0	data.txt
  $ git config core.fileMode true; git status --short
   M data.txt
  git init 时会探测文件系统是否保存权限位:
  $ rm -rf l9probe && git init -q l9probe && git -C l9probe config core.fileMode
  true

在不保存权限位的文件系统上 git 会自动把 core.fileMode 设成 false,此时磁盘权限的变化被忽略。这台机器上 git init 探测到的是 true。

手工指定可执行位

实测 · 本机执行输出
  $ git config core.fileMode false; git add tool.sh; git ls-files -s tool.sh
  100644 1a2485251c33a70432394c93fb89330ef214bfc9 0	tool.sh
  $ git update-index --chmod=+x tool.sh; git ls-files -s tool.sh
  100755 1a2485251c33a70432394c93fb89330ef214bfc9 0	tool.sh
  $ git update-index --chmod=-x tool.sh; git ls-files -s tool.sh
  100644 1a2485251c33a70432394c93fb89330ef214bfc9 0	tool.sh
  $ git config core.fileMode true; git update-index --chmod=+x tool.sh; git ls-files -s tool.sh; git status --short
  100755 1a2485251c33a70432394c93fb89330ef214bfc9 0	tool.sh
  A  tool.sh

fileMode=false 时新文件一律记成 100644;要提交可执行脚本,用 git update-index --chmod=+x 手工设置。

风险

实测 · 本机执行输出
  $ chmod 644 tool.sh; git status --short; git config core.fileMode false; git status --short; echo "(fileMode=false 时 status 为空)"; git ls-files -s tool.sh
   M tool.sh
  (fileMode=false 时 status 为空)
  100755 1a2485251c33a70432394c93fb89330ef214bfc9 0	tool.sh
  $ git config core.fileMode true; git diff --summary
   mode change 100755 => 100644 tool.sh

false 会隐藏真实的权限丢失:已提交为 100755 的文件在磁盘上失去可执行位,status 看不出来。怀疑权限被破坏时,临时切回 true 再看 git diff --summary。

分步演示

文件权限
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / git 2.54.0(Apple Git-157);每个实验在临时目录里新建仓库,HOME 和全局配置指向实验专用的空目录(不读用户自己的 git 配置),提交的作者与时间固定以便复现;需要网络协议的实验用本机的 file:// 或 git daemon 回环端口(9800 以上),不碰任何远程仓库;计时用 time.perf_counter,数字每次运行会有小幅抖动。文件系统是 macOS 的 APFS;umask 为 0022;所有权限数字来自 stat 与 git ls-files -s
    • 没有验证:Windows / FAT / exFAT 等文件系统上 git init 自动设置 core.fileMode=false 的探测过程;core.sharedRepository 对仓库内部文件权限的影响;ACL、扩展属性(macOS 的 @)是否会被 git 记录;git archive 导出时的权限
    京ICP备2026067502号-1京公网安备11010502063487号