Git 推送被拒绝:shallow update not allowed
Git 推送出现 shallow update not allowed 时,确认实际发送端的浅历史状态,从完整来源补全,并检查 CI 同步步骤是否再次截断历史。区分接收端策略与客户端获取选项。
浅克隆仓库能够查看文件、提交修改,但推送到另一个仓库时可能失败:
! [remote rejected] main -> main (shallow update not allowed)
这表示接收端拒绝了需要更新浅历史边界的引用。它不等于所有浅克隆都不能推送,也不能仅凭这句话认定访问令牌权限不足。常见场景包括 CI 同步分支、把浅克隆迁移到新的远端,以及工具将本地仓库推入内部镜像。
需要补全待推送的历史,或者由接收端明确接受浅历史。普通使用者应优先补全发送端的历史,而不是放宽服务器配置。
在真正执行 push 的仓库里确认浅历史
进入失败日志中执行 Git 命令的工作目录,检查:
git rev-parse --show-toplevel
git rev-parse --is-shallow-repository
git remote -v
第二条返回 true 表示当前仓库是浅仓库,返回 false 表示不是。远端列表用于区分“能提供完整历史的来源”和“接收这次推送的目标”;它们不一定是同一个仓库。分享日志前检查远端 URL,去掉可能嵌入其中的凭据。
如果工具会创建临时仓库,或者在容器里重新克隆,外部工作目录的结果不能代表实际发送端。应对日志指出的那份仓库执行检查。当前目录返回 false 时,先核对这一点,不要直接删 .git/shallow。
浅仓库记录了历史在哪些提交处截断。删除记录不会下载缺少的祖先对象,可能让仓库把不完整历史当成完整历史处理;它不能替代补全操作。
从具有完整历史的来源补全
如果 origin 是最初克隆的完整来源,并且仍能提供所需历史,在浅仓库中执行:
git fetch --unshallow origin
git rev-parse --is-shallow-repository
origin 是远端名称示例。若它已经被改成新的空目标仓库,就不适合作为补全来源。先检查远端地址,并选择保留原历史的来源。已有 upstream 远端且确认它保存所需历史时,可以改为:
git fetch --unshallow upstream
git rev-parse --is-shallow-repository
来源完整时,--unshallow 将浅仓库转换为完整仓库。来源本身也是浅仓库时,Git 只能尽可能取得它拥有的历史;命令成功不保证第二条一定返回 false。如果仍为 true,继续核对来源是否完整、当前分支是否来自其他来源,以及实际待推送仓库是否就是这份。
补全会增加网络传输、磁盘占用并更新获取到的 Git 对象和引用。大仓库应预留空间和时间,不要在发布脚本里无限重试。操作前保留重要的未提交文件及本地独有提交的备份;不要把失败处理写成清空仓库后重克隆。
完成后,核对原推送目标和分支,再重试原来的普通推送。不要为了绕过这个错误增加 --force。如果新错误变成非快进或受保护分支拒绝,说明还存在另一项限制,应按新错误处理,不能把强制覆盖远端当作补历史的一部分。
CI 的初次 checkout 完整,不代表后续同步仍完整
使用 actions/checkout 时,默认只获取触发工作流的一个提交。要获取全部分支和标签的历史,可在现有 checkout 步骤的 with 中设置:
fetch-depth: 0
这是要合入既有步骤的参数片段,不是完整工作流;保留项目已选定的 action 版本、分支、目录和认证设置。
不过,后续同步 action 或脚本可能另行执行带 --depth、--shallow-since 的获取,或创建另一份浅仓库。只改第一个 checkout,未必能消除最终推送的浅边界。
把浅仓库检查放在失败的推送步骤附近,并检查中间同步步骤的获取参数。如果中间工具仅允许扩大历史窗口,加深历史可能解决当前需要的祖先缺失,但固定“最近两个月”并不是通用保证:分支分叉点更早,仍可能不够。希望长期依赖完整历史时,应采用工具支持的完整获取方式,并核验最终仓库状态。
换运行器操作系统、反复重新生成令牌都不能补回缺少的 Git 历史。权限问题需要根据对应的权限拒绝信息处理,不要把所有 remote rejected 都归为相同原因。
接收端的 shallowUpdate 不是客户端开关
Git 的 receive.shallowUpdate 控制接收端是否允许为新引用更新浅历史边界。把它设置在自己的发送端仓库,不能改变托管服务或另一台机器的接收策略。
只有确实管理目标仓库、并且业务明确允许保存浅历史时,才应评估接收端设置。这会改变目标仓库接受历史的规则,也不会凭空补齐祖先对象。对于需要完整历史的备份、迁移或镜像,补全来源更符合目标;不要为了让单次推送通过,就把不完整历史当成完整备份。
git fetch --update-shallow 又是另一项行为:它允许获取时更新本地浅边界,不等于 --unshallow,也不会自动允许远端接收推送。不要因名称相近把它们互相替换。
验收与恢复
验收时同时确认实际发送端的浅状态、目标分支是否收到预期提交,以及下一轮自动同步是否仍能工作。单次推送通过,不能证明 CI 下一次不会再次生成浅仓库。
补全历史通常不需要撤销。如果需要恢复原有节省带宽的工作流,恢复之前保存的 checkout 或同步参数,并在独立的新工作目录中建立浅克隆;保留当前仓库中的独有提交和未提交文件。已经成功推送的提交不会因为恢复获取参数而从远端消失,不要用删除分支或重写历史来“回退”一次历史补全。


