科技 · 实用指南
Git 子模块报“not our ref”?替换前先追溯被记录的提交
这个哈希来自超级项目,不是你以为的分支。先记录 gitlink、核对实际子模块 URL,并证明对象是在本地、可远程取得还是确实缺失,再决定 sync、推送、建 fork 或修改固定提交。

“not our ref”不是继续叠加 --force 的提示。它表示超级项目要求远端提供一个精确对象,而当前路径没有交付。修改前先保存这次请求。
安全顺序是记录、路由、对象、修复:找出 gitlink OID,确认生效 URL,验证本地对象与远端可达性,再选择最小且明确的修复。
冻结失败现场并读取 gitlink
不要先执行 rm -rf、deinit、--force 或 --remote。保存失败的超级项目提交、完整错误、子模块路径和本地改动。在超级项目中运行 git ls-tree HEAD -- path/to/submodule。mode 160000 条目记录这个超级项目提交所期望的精确对象。git submodule status --cached 也能显示记录值。
这个 OID 是事故的第一项事实。官方数据模型称它为 gitlink;.gitmodules 中的分支名不能替代该固定值。如果错误显示另一哈希,请在 CI 实际检出的超级项目提交上重复检查,而不是看当前分支尖端。
核对 Git 实际使用的 URL
比较 git config --get-regexp ^submodule\..*\.url$ 与 git config -f .gitmodules --get-regexp ^submodule\..*\.url$。初始化会把 .gitmodules 的 URL 模板写入 .git/config,之后修改 .gitmodules 不会自动更新旧检出。迁移主机或 fork 后,CI 可能拿着正确 OID 去错误仓库。
若超级项目确实变更了 URL,执行 git submodule sync --recursive,再核对生效值。sync 只修复路由,不会创造对象或授予权限。记录 URL 时不要包含凭据;若本地与 CI 路由不同,先修配置或密钥范围。
区分本地存在、对外引用与可取得性
进入子模块仓库,用 git cat-file -e OID^{commit} 判断精确提交是否在本地,用 git cat-file -t OID 查看类型。若只有原开发机拥有它,先保全对象库。常见原因是超级项目先提交了新 gitlink,却没有把对应子模块提交推到可达远端。
用 git ls-remote --heads --tags URL 查询目标远端。它列的是公布引用,不是所有不可达对象,所以没有匹配并不能证明对象已删除。再用相同身份执行普通 fetch 并保存结果。“not our ref”可能表示当前路由或授权无法提供该对象。
按证据选择修复
URL 过期就 sync 或纠正后重试同一 OID。本地有而远端没有,就推到你控制的远端,或建授权 fork 并明确更新 URL;先推子模块对象,再推超级项目 gitlink。若对象在目标主机但 CI 取不到,修复权限或主机策略,不要移动依赖。
只有在 OID 确实不可恢复,或项目有意升级依赖时,才在子模块中检出并测试一个已验证提交,然后 git add 子模块路径并提交新的 gitlink。这是源代码变更,不是缓存修复;记录旧 OID、新 OID、仓库 URL 和理由。
把 --remote 与重克隆当测试,不当证明
普通 update 会取得缺失提交并检出超级项目记录值;--remote 则使用远程跟踪分支。它可能让检出成功,却选择不同提交。只有项目明确采用跟随分支策略时才用,不能称为恢复了缺失固定值。
干净克隆适合在修复后验证可复现性,但不会发布本地提交或修正旧路由。用 git push --recurse-submodules=check/on-demand 预防复发,审查 .gitmodules URL 变更,并让 CI 干净检出精确超级项目提交。
