Git 是一个强大的版本控制工具,但有时在处理大型仓库时回答来意想不到的问题。管理大型仓库的一个常见变通方法是使用「浅克隆(Shallow Clone)」,它仅下载有限的历史记录(例如最新提交),以节省时间和带宽。然而,当你尝试将此浅克隆推送到新仓库时,会遇到一个错误:remote: error: shallow update not allowed。

什么是浅克隆?

浅层克隆是使用 --depth 标志创建的 Git 仓库,该标志会限制下载的历史提交数量。例如:

git clone --depth 1 https://git.6l.ink/lz/lz

此命令仅克隆最新的提交,忽略仓库的其余历史记录。浅克隆存在关键缺陷:

  1. 历史不完整:缺少完整的提交链,导致调试或作者追踪等操作不可靠
  2. 推送限制:默认情况下,你无法将浅克隆推送到新的远程代码库(因此会出现「浅层更新不允许」的错误)

Git 依赖 提交链 来确保数据完整性。每个提交都指向其父提交,形成历史时间线。当您向远程仓库推送时,Git 会检查您正在推送的提交是否在远程仓库中拥有完整的父历史。浅克隆会截断此父链。例如,如果您使用 --depth 1 进行克隆,最新提交的父指针将指向一个在本地仓库中不存在的提交。当您尝试将此提交推送到新的远程仓库(没有历史记录)时,Git 会拒绝推送:因为没有父历史,它无法验证提交的有效性。

解决方案 A: 拉取全量历史记录

要获取缺失的历史记录,使用下方命令可下载浅克隆遗漏的所有提交:

git fetch --unshallow
如果仓库极大,--unshallow 可能因带宽限制而耗时较长或失败。此时可逐步增加深度直至解除浅克隆状态

现在您的克隆已包含完整历史记录,请将分支(例如 main 或 master)推送到新的远程仓库。使用 -u 设置上游分支:

git push -u cnb main

不过,如果新仓库在初始化时已包含 README 或许可证文件,我们会收到 failed to push some refs 错误。可通过合并远程仓库的初始提交到本地分支来解决:

git pull --allow-unrelated-histories cnb main
git push -u cnb main

--allow-unrelated-histories 标志让 Git 合并两个不相关的提交历史记录(您的本地仓库和远程仓库的初始提交)

解决方案 B: 使用补丁

缺点:重写提交哈希,丢失原始历史元数据

在浅克隆中,创建所有变更的补丁:

git format-patch --root HEAD

创建空远程仓库的一个全新克隆:

git clone <new-repo-url> new-repo
cd new-repo

应用补丁:

git am /path/to/shallow-clone/*.patch

推送应用的提交:

git push -u origin main

解决方案 C: Rebase

如果你希望保持代码库原样,包括在浅层初始提交之后添加的新提交,另一个选项是:通过交互式变基来修改此提交。

启动一个包含第一个根提交的交互式变基:

git rebase --interactive --root

将初始提交的 pick 更改为 edit,通常只需要修改第一行,然后保存并关闭文件。

如果你克隆仓库时的深度大于 1,你可能需要对所有这些提交执行相同操作。或者,在交互式变基期间为所有这些提交执行 fixup。

将此提交转换为常规的、非浅层提交:

git commit --amend --no-edit

这也会更改提交 ID,并将你添加为此初始提交的合著者。

完成变基操作:

git rebase --continue