更改文件名大小写后,git 抱怨结帐时可能会丢失数据

After changing the case of a filename, git complains about potential data loss on checkout

基于this answer我使用git mv改变文件扩展名的大小写。

但是现在,每当我尝试更改分支时,都会收到以下错误:

git checkout MyBranch
error: The following untracked working tree files would be overwritten by checkout:
    MyFile/With/The/OldExtension.Ext
Please move or remove them before you switch branches.
Aborting

我仍然可以使用 --force 更改分支,但出于显而易见的原因我真的不想依赖它。

在我看来,git 的索引与现实不同步,但我不确定如何解决它。

我的下一步是什么?

It seems to me that git's index is out of sync with reality

然后,如此处所示,在存储库的新克隆中尝试相同的 git mv,查看那里的索引是否 "less" 被那个不可见的未跟踪文件污染。

TL;DR:您可以在签出所需的提交之前简单地删除文件。

假设您在使用文件名进行大小写折叠的系统上,这里的问题是如果 Git 尝试创建和写入名为 readme.txt 的文件,而文件名为README.TXT 存在,这将覆盖现有的 README.TXT 而根本不会创建 readme.txt

正如 VonC 所说,如果索引 与现实不符,您可以覆盖它。同样的,如果索引中没有任何有价值的东西,可以移除重建:

rm .git/index
git reset --mixed HEAD

这使得索引匹配当前提交(不是当前工作树!)。

这里的主要问题是 Git 的内部结构和 Git 的索引(因为它只是 .git/index 中的一个数据文件),都使用原始字节-可以包含 任何 文件名的字符串,包括例如同时存在的 readme.txt README.TXT。任何 Linux 系统都可以将这两个文件存储在工作树中,1 但典型的 MacOS 或 Windows 文件系统不能。在这种情况下——索引保存 both 这两个名称下的文件可以同时存在于 Linux,但不存在于你自己的系统上——索引保证出无论如何都要与现实同步。

否则——如果索引中只有一个大小写变体——可能只是你的工作树文件名大小写与索引中存储的不同,索引最初与你签出的提交中存储的匹配。 If/when 底层 OS 颠覆 Git 在切换提交时尝试创建的文件名,Git 确定它已创建(例如)readme.txt 并存储它索引中的名称,即使 OS 覆盖了现有的 README.TXT 并将该名称保留在工作树中。

core.ignorecase 设置,Git 在您第一次 git init 存储库(或 git clone 初始化它)时自行设置,记录 OS 处理文件名,以便 Git 知道在 Git 尝试创建 readme.txt 之前检查 README.TXT 是否存在。在这种情况下,您会看到您看到的错误消息,因为 Git 不确定工作树中的 README.TXT 是否真的是 readme.txt它较早提取的那个(也许你删除了那个并在你想保留的地方放了一个不同的 README.TXT)。


1这实际上是依赖于文件系统类型的行为,但是默认的Linux文件系统是区分大小写的。在 HFS/HFS+ on MacOS 上,您可以在文件系统构建时选择文件系统是否区分大小写。我相信NTFS也是如此,并不是说我曾经构建过NTFS文件系统。