Git 未能保持 .vmdk 文件的完整性

Git fails to keep the integrity of a .vmdk file

我一直在尝试跟踪我的 VM 状态,以便我可以随时恢复到旧版本,以防我在测试期间搞砸了。

假设我进入目录,git init; git add .; git commit -m 'restore point'

最初,我有一个名为 Tiny10.vmdk 的文件,大小约为 19GB,然后我将其重命名为 Tiny10_old.vmdk。现在我想我们可以假设 Tiny10.vmdk 不再在 git 记录的工作目录中。所以我尝试了:

git restore Tiny10.vmdk

还原更改

但是,我发现新的 Tiny10.vmdk 现在只有 2GB 的重量,而且肯定已经损坏了。

这是怎么发生的?这是一个错误吗?

git 是否对其可以跟踪的文件有大小限制?

我该如何解决这个问题?

一开始就跟踪 .vmdk 文件是个好主意吗?

P.S.:.vmdk是VMware虚拟磁盘文件的简称


用于重现错误的脚本

git init
git add .
git commit -m 'restore point'
mv Tiny10.vmdk Tiny10_old.vmdk
git restore Tiny10.vmdk

Git 通常 不能 由于各种原因损坏文件 — 但 Git 可以达到其自身的限制. 2GB 和 4GB 大小的数字在某些 32 位机器上尤其神奇,可能包括您的 Windows 系统。如果是这种情况,您将需要不同的 Git and/or 和不同的 OS.

血淋淋的细节

Git 主要是用 C 编写的。1 C 有一些简单的基本数据类型:charshortintlong,以及自 1999 年以来的 long long,以及可应用于这些类型的有符号和无符号限定符。没有从这些 C 类型到 machine-level 硬件指令的 规定 映射,但是有一组非常通用的原则用于避免此处出现意外:char(和它的有符号和无符号变体)映射到一个 8 位字节,它只能存储从 0 到 255 的值(或 -128 到 +127,当有符号时),short 映射到一个 16 位“短字” " 范围从 0 到 65535 或 -32768 到 +32767,int 映射到 16、32 或 64 位,long 至少是 32 位类型,范围从 0 到 4294973647 或 -2147483648到 +2147483647,并且 long long,如果它存在于您的实现中,是一个从 0 映射到 264-1 或 -2[=47= 的 64 位类型]63到+263-1.2

Git 使用的 C 代码长期以来都是 C99 之前的代码,避免直接使用 long long。这是我认为现在终于放松了(尽管其他 C99 导入,例如 for 循环内的声明仍然被避免),但是如果我们 do 避免 long long 和 max long,此 可能 最大为 4 GB(4294973647 或 0xFFFFFFFF)。签名后,它可能 最大为 2 GB。将 1 加到包含最大可能值的 unsigned long 会产生一个包含零的变量(换句话说,我们期望的通常的有限域算术结果)。当使用有符号数时,C 没有规定我们是否得到这种环绕、溢出陷阱、“粘性”算法或其他任何东西,但为了与流行的实现兼容,我们通常会看到相同类型的环绕,因此 2147483647 + 1 等于 -2147483648(0x7FFFFFFF + 1 = 0x80000000,然后将其视为最负的二进制补码值)。

当 C-based Git 实现具有这些 32 位限制时,最大可能的文件大小为 2 GB 或 4 GB(减一),具体取决于文件大小是否存储在有符号或无符号整数中。理想情况下,Windows 系统上的 C Git 至少应该 注意到 某些文件大于它的存储能力,并给你一个错误,而不是接受它大小 mod 231 或 232 并使用它并假装一切都很好。您可能会考虑将此作为针对您的特定 Windows 版本的 Git.

的错误进行归档

1有一个 JGit Java 版本,一个 Go 版本,显然还有一个出现在 Rust 和其他语言中.但是 Git-for-Windows 和不合格的 "Git" 往往指的是 C 版本。 每个版本都有自己的怪癖,所以如果您遇到怪癖并且您可能使用的不是 C Git,请查明您使用的具体版本。即使你用的是CGit,它也有很长的历史,各种版本都有各种bug,所以看你用的是什么版本。

2这些是我凭记忆输入的,输入时没有仔细看,所以要小心打字错误。使用二进制计算器求出 232 等以仔细检查精确值。