DEBUG.WIKI
V.PS:让项目靠近用户,亚太、欧洲、北美 KVM VPS
IPPeak:从目标地区访问网页,住宅代理、SEO 监测、广告验证
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国

Git 已设置 safe.directory,后续命令仍报 dubious ownership

checkout 成功,后续 Git 命令仍报 dubious ownership 时,在失败步骤中核对用户、HOME、仓库路径和配置来源,只为确认可信的目录添加例外。

代码已经 checkout 成功,下一步运行 git status、提交脚本或构建工具,却报 fatal: detected dubious ownership in repository。日志里能找到添加 safe.directory 的命令,后面的 Git 却仍拒绝访问仓库。

要在失败进程所在的环境里检查。宿主机、checkout 步骤和后续容器可能使用不同用户、不同 HOME,也可能把同一个仓库挂到不同路径。前一步写入的信任配置,后一步未必读得到。

下面的检查命令适用于 Linux 和 Linux 容器。路径 /workspaces/example 是示例,执行前换成报错里的实际仓库路径;涉及信任设置的步骤,只适用于你确认由自己或可信流程管理的仓库。

先确认错误发生在哪一步

如果失败的是 checkout 本身,先检查正在使用的 action 版本、输入参数和它的完整日志。如果 checkout 已成功,只有后面的容器或脚本失败,则在那个容器或脚本里继续排查,不要只检查 runner 宿主机。

runner 与后续容器的用户或 HOME 不同时,Git 可能读到不同的信任配置。检查应放在失败的容器或步骤内,同时核对实际仓库路径;不能只凭这一条错误认定是 HOME 问题。

当前 checkout 启用 set-safe-directory 时,会把路径加入临时全局配置,并为相关 Git 调用准备临时 HOME。因此,“checkout 配置过了”还不足以说明另一个 action 或进程能使用同一份设置。

在失败的环境里核对身份和路径

进入报错的容器,或把以下检查放到失败步骤的 Git 命令前面:

git --version
id
printf 'HOME=%s\n' "$HOME"
pwd -P
stat -c '%u:%g %n' /workspaces/example /workspaces/example/.git

id 给出当前进程身份,stat 显示仓库目录和 .git 的数字 UID、GID。比较当前 UID 与所有者 UID;不要只比较用户名,因为宿主机和容器里的同名用户可能对应不同数字。.git 在 linked worktree 中可能是文件,需要进一步检查它指向的 Git 目录。

如果错误路径是 /github/workspace,就检查这个路径,不要把宿主机上的 /home/runner/work/... 直接填进去。Docker 的绑定挂载会把宿主机路径映射到容器内的目标位置。Git 要判断的是当前进程看到的仓库。

如果这是个人仓库,却因之前用 root 操作或容器 UID 配置错误而变了所有者,应先修正工作流程。容器进程身份可以由镜像的 USER 指令指定,运行时也可能另有覆盖。不要对共享挂载直接递归 chown:变更可能影响宿主机和其他服务;也不要用 chmod 777 代替所有者检查。

看实际读到的是哪一份全局配置

仍在失败步骤的用户和环境中,读取全局例外列表:

git config --global --show-origin --get-all safe.directory

它会显示配置来源和已有路径。没有匹配值时,命令可能不输出内容并返回非零状态;这表示没有读到该键,不能单凭这一点判断仓库损坏。如果同时出现配置文件读取错误,则先处理那个错误。

将输出的配置文件位置与添加例外时的日志对照。常见偏差是:例外写进 root 的配置,失败命令却由普通用户执行;或者前一步用了临时 HOME,后一步恢复了原 HOME。还应检查 GIT_CONFIG_GLOBAL 是否指定了另一份文件,以及步骤是否设置了不同的 XDG_CONFIG_HOME。

safe.directory 只接受受保护配置中的设置。把它写进仓库自己的 .git/config,不能让这个仓库自证可信。system、global 和 command 属于受保护作用域。下面采用当前执行用户的 global 配置,便于检查和撤销。

只为确认可信的仓库添加例外

确定仓库内容和管理者可信、所有者不同是预期安排后,在失败进程所在环境添加它实际看到的路径:

git config --global --add safe.directory /workspaces/example
git config --global --show-origin --get-all safe.directory
git -C /workspaces/example status --short

不要加 sudo,否则可能写进另一个用户的配置。目录必须事先核对;safe.directory 不会修改文件所有者、赋予写权限,也不会修复凭据问题。

GitHub Actions 中,如果失败的是普通 run 步骤,并且仓库就在该步骤的 GITHUB_WORKSPACE,可把下面这段放到该步骤的 Git 操作之前:

git config --global --add safe.directory "$GITHUB_WORKSPACE"
git -C "$GITHUB_WORKSPACE" status --short

若 checkout 使用了自定义 path,应填写实际仓库路径。如果失败的是 Docker action,就要在该 action 容器中完成设置;不能假定前一个宿主机 run 步骤写入的配置会传进去。添加主仓库也不意味着所有子模块都已获信任:后续错误若指向另一个仓库,应对那个路径重新核对。

网上常见的 safe.directory=* 会关闭这项所有者检查,而不是只授权当前仓库。对于已知的单个工作目录,不需要把所有仓库都加入信任范围。

确认修复,并留下撤销办法

用原先失败的用户、容器和步骤重新执行检查。git status --short 成功且没有输出,可能只是工作区没有改动;不能把空输出当成失败。随后重跑原先的构建或 Git 操作,确认没有新的权限、认证或分支错误。

如果重新启动容器或下一次 job 又报错,检查例外是否只写在临时文件系统里,以及新进程是否仍使用同一用户、HOME 和路径。应把必要设置放入可信的初始化流程,不能只修一次交互式 shell。

不再需要这个例外时,先保存现有配置,再仅删除对应路径:

git config --global --fixed-value --unset-all safe.directory /workspaces/example
git config --global --show-origin --get-all safe.directory

--fixed-value 按完整字符串匹配,保留其他不同路径的例外。若这条记录本来就不存在,删除命令会返回非零状态。删除例外后,再确认脚本使用的是预期用户。