我最近发现我的解决方案中有几个文件夹在Git中有两个不同的路径(GitHub显示两个单独的文件夹),一个是FooBar
,另一个是Foobar
。这是因为有些文件使用以前的文件夹名称作为路径注册,有些文件使用后者注册。
这是在本地(在Windows中)通过将Git配置为不忽略大小写而发现的:git config core.ignorecase false
我通过删除整个文件夹,提交,然后重新添加文件夹并再次提交来修复此问题。这解决了问题,但是路径更改的文件丢失了Git History 。针对这些文件的新路径运行gitk
只显示了一个提交。在他们的旧路上行驶gitk
揭示了他们的整个历史。
下一步:使用git mv
移动文件:
git mv Foobar/file.txt FooBar/file.txt
这会产生错误:
fatal: destination exists, source=Foobar/file.txt, destination=FooBar/file.txt
如果我先尝试删除文件,当然Git会抱怨源文件不存在。
然后我发现,如果将-f
添加到mv
命令,Git不会抱怨已经存在的目标。但是,在提交重命名后,gitk
表明历史已被切断了!
我甚至尝试过three step dance described here,但这只是-f
的另一种方式。结果相同。
基本上我只想以某种方式在不区分大小写的操作系统中将文件从Foobar/file.txt
移动到FooBar/file.txt
,同时保留Git历史记录。这可能吗?
答案 0 :(得分:1)
真正的问题没有简单的解决方案。
在Git中,文件不具有历史记录。提交有历史 - 或者更确切地说,提交是历史。这就是历史。对于Git来说&#34;关注&#34;一个文件,如在git log --follow <path>
中,Git一次查看提交,将每个提交与其父提交进行比较。
如果父级和子级之间的差异显示父级包含名为parent/path/to/pfile
的文件,并且子级包含名为child/path/to/cfile
的文件和这两个文件的内容,在这两个提交中,&#34;足够相似&#34; (这里必须有几个条件),然后,在Git&#34;眼睛&#34;中,父对子过渡表示该文件的重命名。因此,在此时,git log --follow
一直在寻找child/path/to/cfile
,而是开始寻找parent/path/to/pfile
。
如果没有--follow
,则git log
不会执行此操作&#34;找到重命名&#34;操作......一般来说,Git认为任何具有任何字节级差异的路径名都代表不同的文件。换句话说,不会发生大小写折叠和UTF-8归一化。考虑,例如,单词schön
,可以表示为s
c
h
ö
n
或s
{ {1}} c
h
合并 - o
¨
。我们可以在Linux机器上使用这两个不同的UTF-8样式名称创建两个不同的文件。运行n
将显示两个名称相同的文件:
ls
Git非常乐意分别存储这两个文件。但是,MacOS拒绝允许两个文件共存,就像Windows一样 - 默认情况下,MacOS也拒绝允许$ cat umlaut.py
import os
p1 = u'sch\N{latin small letter o with diaeresis}n'
p2 = u'scho\N{combining diaeresis}n'
os.close(os.open(p1.encode('utf8'), os.O_CREAT, 0o666))
os.close(os.open(p2.encode('utf8'), os.O_CREAT, 0o666))
$ python umlaut.py
$ ls
schön schön umlaut.py
和Foobar
共存。
让Git将文件存储在新字节序列下的新提交中,并保留历史 ,它不是您想要保留的历史记录。但是
在实践中,您可能只需要在Git眼中重命名该文件 - 这对您操作系统眼中的文件名无任何影响; FooBar
和FooBar
在此处相同的名称,并继续使用。您可以选择将所有历史记录重新编辑到最初添加到存储库中的错误配对,通过复制(稍作修改)每个&#34;坏&#34;承诺一个新的和改进的&#34;好&#34;承诺。但这意味着让使用回购的所有人从&#34;糟糕的旧回购&#34; to&#34;新的和改进的好回购&#34;。