git不区分大小写的目录已重命名

2ul0zpep  于 11个月前  发布在  Git
关注(0)|答案(3)|浏览(144)

希望解决一个问题,我现在有2个目录路径相同的名称只是不同的大小写。因为,我重命名的目录,以candidate,一旦在一个时间。
我只看到一个文件夹/目录中列出的文件夹视图,没有给其他文件和文件夹在根.“someproject”/"src*”
然而,当进行更改时,我看到2组要提交的文件。

Src/somefile.txt
src/somefile.txt

字符串
如何从存储库中删除Src文件夹和内容?

  • 我会备份“src",删除“* src *”文件夹,提交,然后将其添加回,提交,推送吗?
7xzttuei

7xzttuei1#

nobalG's answer很好

如果您跑步:

git rm -r --cached src
git add src

字符串
你应该可以开始了(用git status确认)。你不需要向.gitignore添加任何东西。你也可能实际上不需要git add任何东西。
这个答案的问题在于,它只是一个食谱;它没有告诉你 * 下一次 * 你遇到各种问题时该怎么做。

首先,让我们正确地定义问题。我们需要注意以下几点:

***Git存储的是 commits,而不是文件。**也就是说,当你运行git checkoutgit commit时,你处理的存储单元是 commit。的确,commits * 包含 * 文件,但这是一个打包处理。(有一些有点笨拙的方法来进行零碎的工作,我们稍后会谈到;它们在特殊情况下很有用。)
***任何提交的所有部分都是只读的。**这包括所有存储的文件。它们以永久冻结的压缩格式存储。每个文件的内容也会与其他存储的文件进行重复数据删除。这很重要,因为 * 每个提交都会存储每个文件的完整副本 *,但通过重复数据删除,这意味着大多数提交几乎都与其他提交共享所有文件。文件是只读的这一事实使其能够:从字面上看,不可能 * 更改 * 一个存储的文件,所以如果提交X的文件F1需要与提交Y的文件F2相同的内容,那么它们实际上使用相同的存储内容是完全合理的。
***你看到的和使用的文件 * 不在Git中 *。**在Git中 * 的文件 * 不是你看到的和使用的文件 。这是上一条的直接后果。在任何给定的提交中,存储的文件都是以一种 * 只有Git可以读取 * 的形式存在的, 字面上没有任何东西-甚至Git本身-可以覆盖它们 *,这意味着在Git中的文件对于完成任何实际工作都是完全无用的。所以Git不会尝试这样做。当你选择一个提交来处理时,Git会将提交中的文件复制到一个工作区中,我们称之为“工作树”或“工作树"

现在我们明白了你可以看到和使用的文件 * 不在Git中 *,而在Git中 * 是 * 的文件是以一种特殊的Git特有的形式存在的,现在我们可以理解这里发生了什么。当Git存储文件时,这些文件 * 不在文件夹中 * 并且 * 可以有几乎任意的名称 *。在Git提交中,我们没有一个名为srcSrc的 * 文件夹 * 来存储一个名为somefile.txt的 * 文件 *。相反,我们只有一个名为src/somefile.txt的 * 文件 ,其中有一个斜杠。1而且-这是我们这里问题的关键-- 这些名称总是区分大小写 *,因此提交可以很容易地将src/somefile.txt * 和 * Src/somefile.txt作为两个单独文件包含。提交也可以同时包含readme.mdREADME.md,依此类推。
同样,这些文件不是普通的文件。它们以一种特殊的、只读的、Git专用的、压缩的和重复数据消除的格式存储,只有Git可以读取(除了一次初始创建以进行新的提交之外,什么都不能写)。而且,它们区分大小写,可以拼写出你在Windows机器上实际上无法使用的文件名。2
如果您使用的是典型的Mac或Windows系统,则您的 * 常规 * 文件是保留大小写但不区分大小写的。这实际上是特定于文件系统的,并且很容易设置一个macOS可挂载磁盘,其中的文件是区分大小写的,这样您就可以 * 存储readme.md * 和 * README.md。请参阅my answer here了解在macOS上执行此操作的方法。在Windows上,你可以设置一个虚拟机并运行Linux;我不使用Windows,所以我没有现成的过程。
1从技术上讲,committed 版本的文件在内部使用类似于文件夹的结构;文件夹的位置是Git的 index。但是你不能直接处理提交:你把现有的提交读到Git的index中,然后在Git的index中建立新的提交,这里的文件名实际上包含了斜杠。所以我们不妨说文件名中有斜杠。
2 Mac用户可以嘲笑Windows用户无法创建名为aux.txt的文件,因为在Mac上创建aux.txt很容易。但Mac用户还有其他问题:例如,有两种拼写文件名agréable的方法,其中只有一种在Mac上有效。虽然有时这是一件好事-我们可能不希望有两个不同的文件似乎具有相同的名称-它带来了完全相同的互操作性问题。

在macOS上设置问题案例

由于我手边有一台Mac,我可以举例说明我们如何设置一个问题案例。我们从一个新的、完全空的仓库开始:

sh-3.2$ cd ~/tmp && mkdir case && cd case && git init
Initialized empty Git repository in /Users/torek/tmp/case/.git/
sh-3.2$ mkdir Dir && touch Dir/file && giit add Dir/file && git commit -m initial
[master (root-commit) 07ed87d] initial
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 Dir/file
sh-3.2$ mv Dir dir
sh-3.2$ echo some contents > dir/file

现在,到目前为止,我们所做的在Git本身是创建一个初始空提交,其中有一个名为Dir/file的文件。然而,最后两个命令告诉macOS修改我们的 * 工作区 *-我们的工作树-将Dir,initial-token重命名为dir,token,运行ls表明操作系统确实重命名了工作树目录,但是Git知道 * 在这个文件系统上 *,dir/fileDir/file都可以 * 读取或写入同一个普通文件 *。所以:

sh-3.2$ ls
dir
sh-3.2$ cat dir/file
some contents
sh-3.2$ cat Dir/file
some contents


你可以看到我们做的更新。但是:

sh-3.2$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   Dir/file

no changes added to commit (use "git add" and/or "git commit -a")


Git知道dir/fileDir/file访问同一个文件。此外,Git知道我们的 last commit中有一个空的Dir/file。因此,Git * 假设 *,我们希望保留文件名的先前拼写:Dir/file。(注意,名称中有斜杠:没有文件夹,只有文件。)

深入了解:Git的index和core.ignorecase

这里涉及到多个相互交织的部分:

  • 首先,当我们“检出”某个提交时,Git会建立一个数据结构,Git会将其称为“索引”、“暂存区”,或者现在很少使用的“缓存”。这三个名称反映了这件事的重要性,或者“索引”不是一个很好的名称,或者两者兼而有之。

我喜欢将Git的索引总结为 your proposed next commit。这是它作为 staging area 的角色。但它并不完全完整,我们稍后会看到。尽管如此,这里的关键思想是,当你第一次检查某个特定的提交时,Git * 复制 * 所有提交的文件 * 到 * 它的索引。2索引--建议的 * 下一次 * 提交--现在匹配 * 当前 * 提交。如果你做了更改,你必须让Git把更新后的文件复制到索引中,这就是git add的作用。
请记住,索引中有文件的 * 全名 *,并带有正斜杠。这些文件都保存在数据文件中--目前是.git/index,可能还有.git目录中的一些其他文件--这些文件不是由操作系统管理的,而是由Git管理的。所以它们由Git决定,Git说这些文件名区分大小写的,因此,索引可以同时存储readme.md * 和 * README.md,或者同时存储dir/file * 和 * Dir/file

  • 同时,你的 * 工作树 * 或 * 工作树 * 中有你所有的文件,扩展到可用的形式。但是如果你的操作系统将这些文件视为区分大小写,您将无法在此处同时存储readme.md * 和 * README.md。您将无法同时存储dir/file * 和 * Dir/file。您的操作系统将dirDir视为 * 一个文件夹名 *,readme.mdREADME.md都是一个 * 文件名 *。所以事实上,*Git的提交可以在这里保存两个名称 *,但 * 你的操作系统的文件系统不能 *,这导致了我们的问题。
  • 最后,当Git设置Git仓库时-例如在上面的例子中的git init时间-Git * 探测你的文件系统是否忽略了大小写 *。如果你的操作系统 * 忽略了大小写,那么这个问题就会发生,Git会将core.ignorecase设置为true
sh-3.2$ git config --get core.ignorecase
true


Git使用它来“知道”Dir/filedir/file,虽然与 Git 不同,但与 * 您的主机操作系统的文件系统 * 相同。
2索引中的实际内容是:

  • 文件名,用正斜杠完成;
  • 文件的 * 模式 *,通常是100644(读/写)或100755(读/写,但也可执行);
  • 一个 blob hash ID,它的作用类似于文件内容的链接,已经删除了重复数据,并且是内部Git格式;以及
  • 标记和其他内部缓存数据来帮助Git跟踪工作树中的内容。

由于内容实际上是在其他地方,索引并不持有文件的真正副本。然而,副本管理是全自动的。这意味着索引副本就像文件的副本一样,如果可能的话,不占用任何磁盘空间(如果内容重复了一些现有的提交文件)。
所有这一切的最终结果是,每个文件的索引“副本”都准备好进入 * 下一次 * 提交。换句话说,索引中的内容 * 就是 * 暂存的内容。但是如果你有一万个文件,当9997个都是一样的时候,把它们都列出来会很痛苦和无聊。所以git status并没有告诉你这9997个 * 相同 * 的文件。它只是说这3个 * 不 * 匹配的文件是“staged for commit”。事实上,这一万个文件都是staged的;我们只是保持git status可用,而不是 * 说 * 任何关于 * 匹配 * 的东西。

Git如何使用core.ignorecase

所以,在上面的设置中,我们:
1.在不区分大小写的Mac或Windows系统上创建了一个新的空存储库,其中不能同时包含README.md * 和 * readme.md文件,也不能同时包含Dir/file * 和 * dir/file。如果我们有一个名为Dir(x1)的目录,并且我们尝试创建dir/another,则 system 将创建Dir/another
这不是Git的问题,但Git必须处理它。

1.创建并提交了一个空的Dir/file。这现在在一个提交中。它永远不能被更改!这个提交,只要它存在,就有那个路径名,有那个特定的大小写。
1.使用一些操作系统端工具(macOS上的普通mv,我不确定你在Windows上会使用什么)将Dir重命名为dir
由于主机文件系统的不区分大小写的特性,在我们创建仓库时,Git自己将core.ignorecase设置为true。我们现在可以 * 欺骗Git*,说我们的操作系统将这些视为 * 不同的 * 情况。这不是真的!但我们 * 可以 * 实际上欺骗Git,故意的,暂时的。如果出了问题,我们将负责:别这么做除非你愿意承担责任。

sh-3.2$ echo some contents > dir/file
sh-3.2$ git status --short
 M Dir/file
sh-3.2$ git config core.ignorecase false
sh-3.2$ git status --short -uall
 M Dir/file
?? dir/file


(the第一行是重复的,但是因为我使用了>,所以我可以尽可能多地重复它--它会擦除文件,并在其中插入一行阅读some contents)。
在这里,我使用git status --short来缩短输出。M表示工作树副本已经被修改。在欺骗Git之前,Git检查并看到dir/file存在,* 发现 * 这与索引Dir/file匹配,并将它们匹配起来。然而,一旦我欺骗Git,有趣的事情发生了:

  • Git相信有一个Dir/file。它试图打开那个文件,假设它不会得到dir/file。但是它确实得到了dir/file。它将这个dir/file与它认为得到的Dir/file进行比较,发现它是不同的。所以现在Git说这个文件是 * 修改的 *(在工作树中状态为M)。
  • Git找到了名为dir的目录/文件夹,读取它,并找到了名为file的文件。在Git的索引中,路径dir/filenot,所以Git说这个文件是 untracked(这是双问号)。

我现在可以git add dir/file

sh-3.2$ git add dir/file
sh-3.2$ git status --short
 M Dir/file
A  dir/file


Git使用文件名将dir/file复制到索引中。* 此 * 文件副本的内容为some contentsgit status输出现在显示为 new 文件。这是因为 * 当前提交 * 没有dir/file;它只有Dir/file(具有不同的内容)。
我现在可以创建一个新的提交了。这个新的提交包含Dir/file-仍然是空的-和dir/file,内容为some contents

sh-3.2$ git commit -m 'add lowercase version with content'
[master 793d32c] add lowercase version with content
 1 file changed, 1 insertion(+)
 create mode 100644 dir/file
sh-3.2$ git show HEAD:Dir/file
sh-3.2$ git show HEAD:dir/file
some contents
sh-3.2$ git ls-tree -r HEAD
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391    Dir/file
100644 blob f70d6b139823ab30278db23bb547c61e0d4444fb    dir/file


这里的blob行是Git表示有两个文件的方式。这两个文件的模式是100644(读/写但不执行)和丑陋的哈希ID是Git消除重复内容的方式;文件的名称出现在右边。git ls-tree-r选项需要让它显示每个“文件夹”中的内容(另见脚注1:如果不是因为索引将文件夹删除,Git * 将 * 能够存储一个空文件夹,但因为索引做了它自己的事情,Git不能)。
现在我已经提交了 this,这个冻结状态--有两个不同的名字--cases--永远存在,或者至少,只要第二次提交继续存在,我就能做到这一点,即使是在不区分大小写的Mac文件系统上,通过欺骗Git的技巧。
在恢复core.ignorecase之前,我现在可以做最后一个技巧:

sh-3.2$ git rm --cached Dir/file
rm 'Dir/file'
sh-3.2$ git status --short
D  Dir/file
sh-3.2$ ls
dir
sh-3.2$ ls dir
file


实际上我已经可以恢复core.ignorecase了,因为git rm --cached不需要做花哨的大小写技巧:它总是直接从Git的索引中删除条目,而且这些条目总是区分大小写的。但是我想我应该像这样展示它。让我们把core.ignorecase放回它应该的样子,然后提交结果:

sh-3.2$ git config core.ignorecase true
sh-3.2$ git commit -m 'remove uppercase Dir/file'
[master c5edc17] remove uppercase Dir/file
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 Dir/file
sh-3.2$ git status
On branch master
nothing to commit, working tree clean


我做了一个正确的提交,包含了我想要的内容,现在我有了一个可以在Mac上正确使用的提交,即使是在一个不区分大小写的文件系统中。中间的提交,包含Dir/file * 和 * dir/file,* 不能 * 在典型的Windows或Mac设置中正确使用-但是因为Git在进行新提交时实际上使用了 index,我们可以,这并不有趣,更好的工具可能会更好,但这显示了如何克服一些问题。
请注意,当你关闭core.ignorecase时,* 你 * 有责任让文件名大小写正确。Git会错误地认为创建dir/file会创建dir/file,即使存在一个名为Dir/的文件夹。你必须手动rm -r整个Dir目录,或者重新命名它:

mv Dir save
git checkout -- dir/file


例如,这样当Git去创建dir时,操作系统就不会轻松地使用现有的Dir/目录。
在您完成这种手动的、仔细的、一次一个文件的挖掘之后,重新打开core.ignorecase绝对是一个好主意(假设它最初是打开的:使用git config --get来找出答案)。
请注意:

git checkout <commit> -- <path>

告诉Git从 * 某个特定的提交 * 中提取给定的<path>
1.将其复制到Git的索引中:关闭core.ignorecase后,Git会认为使用不同大小写的类似名称是可以的;
1.将生成的索引文件复制到你的工作树中;这是 * 你 * 有责任确保任何现有文件夹都有正确的大小写的地方。

这就是我上面所说的“零碎”,在长部分的顶部的方式。形式:

git checkout -- <path>

告诉Git从 * Git索引中的当前内容 * 中提取给定的路径。
我们在一种情况下使用提交说明符,而在另一种情况下省略它,这是令人困惑的。如果你有Git 2.23或更高版本,你可以使用git restore而不是git checkoutrestore命令允许你指定某些内容的来源为提交或索引。如果你选择一个提交,你可以指定文件是否被复制到索引,或者你的工作树,或者两者。如果你选择索引作为源,你唯一可以复制文件的地方就是你的工作树。
(The最后一点是因为你的工作树和Git的索引都可以被写入,但是提交只能被读取。

qnyhuwrf

qnyhuwrf2#

从git中删除目录,但不删除文件系统

git rm -r --cached folderNameToRemove

字符串
将目录添加到.gitignore,然后记住在那之后做一个git push。
对于类似场景,checkout this brilliant thread

yeotifhr

yeotifhr3#

从1.5.6版本开始,.git/config的[core]部分中有一个可使用的“删除”选项。
例如
add_release = true
要仅为一个repo更改它,请从该文件夹运行:
git config core. configure true全局修改:
第一个月

相关问题