总结
您需要理解 Git 存储每个文件的至少三个,有时最多五个活动副本的想法:一个在当前提交中, index 中的一个(或两个或三个!),以及一个 - 您可以看到和使用的唯一一个 - 在您的 work-tree 中。 git ls-files 命令查看这些副本,然后根据您提供给 git ls-files 的标志告诉您其中的一些内容。
如果没有每个文件 3 到 5 个副本的想法,Git 中的很多东西都将毫无意义。 (嗯,即使 使用它,有些事情仍然很棘手,但这完全是另一个问题。?)
长
我认为这里有两个问题。一个需要一些术语,然后另一个应该到位:
[git ls-files] 是否显示本地存储库中的文件,
有点,但是:
临时存储库,
Git 没有暂存存储库。在不同的 Git 文档中,每个存储库都有被称为 index 或 staging area 的东西。 (还有一个已过时的第三个名称,cache,它也出现在 the Git glossary 中。)
远程仓库
绝对不是:根本不需要任何远程存储库(即其他 Git 拥有自己的存储库),如果有的话,只有 git fetch 和 git push 让您的 Git 调用他们的 Git 并与之交换数据他们。 (好吧,git ls-remote 执行git fetch 的第一个点,git pull runs git fetch,所以这两个也与遥控器交换数据。但git ls-files 没有。 )
还是来自其他地方?
是的,有点。这让我们回到了第一部分。因此,让我们采用the Git glossary 中定义的这三个术语。下面的斜体(包括粗斜体)文本直接来自链接文档:
提交被永久冻结
当您运行 git commit 时,Git 会为您的所有文件(好吧,无论如何,所有跟踪 文件)制作快照并存储这些快照,以及一些元数据,例如您的姓名和电子邮件地址,在一次提交中。这个提交大部分是永久的——你可以摆脱提交,通常有相当多的困难,但为了方便起见,将它们视为永久的——并且完全,完全,100% 只读。故意像这样只读,因为这允许其他提交共享相同的文件副本,所以如果你提交同一个文件一次,十次,甚至一百万次,真的只有一个 存储库中该文件的副本。只有当您将文件更改为新版本时,Git 才必须提交一个新的、单独的副本。
提交被编号,但不是通过一个简单的顺序编号系统。也就是说,我们可以将它们画为一系列简单的数字或字母:
... <-C4 <-C5 <-C6 ...
之后的每个提交都指向其直接前任。但它们的实际名称是大而丑陋的哈希 ID。每一个都保证是独一无二的,这就是为什么它们必须这么大、这么丑而且看起来很随意。每个哈希 ID 实际上是一个加密校验和,根据提交的内容计算,因此宇宙中的每个 Git 都会同意 那个 提交,并且只有那个提交,得到 那个校验和。这就是您(甚至 Git)无法更改它的其他原因:如果您从存储库数据库中取出一个提交,对其进行修补,甚至更改一个位 然后将其放回数据库中,您会得到一个 new 提交,并带有一个新的和不同的哈希 ID。
所以提交被完全冻结,永远。它们里面的文件也被永远冻结,并被压缩,并且是一种特殊的 Git-only 格式。我喜欢称这些文件为“冻干文件”。 this 的意思是,嘿,它们非常适合存档,但它们对于完成任何 新 工作完全没用......和 那个 em> 意味着 Git必须 提供一些方法来获取这些冻干文件并将它们重新水化为有用的形式。
工作树提供有用的形式副本
没有比这更简单的事情了:工作树包含文件的有用形式、重新水化的副本。因为它们只是您计算机上的普通日常文件,您可以查看、使用它们,随意更改它们,或者以其他方式使用它们。从技术上讲,它们根本不在存储库中——它们更接近于它。在典型设置中,存储库本身位于工作树顶层的 .git 目录/文件夹中。
显然,如果您已提取到make 工作树的提交,那么现在每个文件必须有两个副本:冻干提交的一份,加上正常工作的一份。 Git 可以到此为止。 Mercurial确实到此为止:如果您使用 Mercurial 而不是 Git,则无需担心第三个副本,因为没有第三个副本。但 Git 继续存储更多的文件副本。
索引/暂存区位于提交和工作树之间
Git 在这里所做的是在冻干的提交副本和工作树副本之间插入每个文件的 第三个 副本。这第三个副本是提交文件格式——即预脱水——但由于不在提交中,它实际上并没有完全冻结:它可以在替换随时。这就是git add 所做的事情:git add 从工作树中获取文件的普通副本,将其压缩为冻干格式,然后替换索引中的副本。或者,如果文件根本不在索引中,它会将副本放入索引中。
这就是为什么你必须一直git add 文件。在 Mercurial 中,您只需 hg add 一个文件一次。之后,您只需运行hg commit,Mercurial 就会查看它知道的所有文件,并将它们冻结到一个新的提交中。在大型存储库中,这可能需要很长时间。相比之下,Git 已经在索引中拥有它应该知道的所有文件,并且已经脱水,因此git commit 可以将这些脱水文件打包成一个新的冻结提交。这种速度的代价是git add,但如果你对索引副本玩一些巧妙的技巧——例如,使用git add -p——你将获得比只是加速更多的好处。
正如the Git glossary 在对索引的描述中提到的那样,索引在冲突合并期间扮演了扩展的角色。当您执行合并操作时——无论是来自git merge,还是来自git revert 或git cherry-pick 或任何其他使用合并引擎的Git 命令——并且它并不顺利,Git 最终会将所有三个输入用于每个文件都放入索引中,这样您就可以得到三个而不是file.ext 的一份副本。但只要您不在合并中,索引中就只有一个副本。
通常索引副本与HEAD 冻结副本匹配,或与工作树副本匹配,或两者兼而有之。例如,在新的git checkout 之后,所有三个副本都匹配。然后在工作树中修改file.ext:现在提交和索引匹配,但它们与工作树副本不同。然后你git add file.ext,现在索引和工作树匹配,但它们与冻结副本不同。然后你git commit 进行一个新的提交,它成为当前的提交,并且所有三个副本再次匹配。
请注意,您可以修改工作树副本:
vim file.ext
然后将更新后的复制到索引中:
git add file.ext
然后再次编辑它:
vim file.ext
这样,您可以制作所有三个副本不同。如果你这样做,git status 会说你有更改暂存等待提交,因为索引副本与当前提交副本不同,并且说你有更改没有 em> 暂存提交,因为工作树副本与索引副本不同。
工作树可以包含根本不在索引中的文件
索引最初只是当前提交的副本。然后 Git 还将这些文件复制到工作树中,以便您可以使用它们。但是您可以在工作树中创建文件,不在它们上运行git add。这些文件现在不在索引中,如果您运行 git commit,它们也不会在新提交中,因为 Git 会从索引构建新提交。
您还可以从索引中删除文件,而无需从工作树中删除它们:
git rm --cached file.ext
删除索引副本。当然,它不能触及当前的提交冻结副本,但是如果您现在进行 new 提交,则新提交中根本不会包含 file.ext。 (当然,之前的提交仍然有效。)
现在在你的工作树中的任何文件现在,并且不在你的索引现在 >,是一个未跟踪文件。它的未跟踪性来自它不在您的索引中的事实。将该文件放入您的索引并对其进行跟踪,无论您如何将其放入索引中。将其从索引中删除,无论您如何将其从索引中删除,它都不会被跟踪。这就是索引的最后一个作用:确定哪些文件被跟踪,因此将在下一次提交中。
现在我们可以清楚地看到git ls-files 做了什么
git ls-files 所做的是读取所有内容:提交、索引、和工作树。根据您向 git ls-files 提供的参数,它会打印索引和/或工作树中的部分或所有文件的名称:
git ls-files --stage
列出索引/暂存区域中的文件及其暂存槽编号。 (它没有说明HEAD 提交和工作树中的副本。)或者:
git ls-files --others
列出工作树中但不在索引中的文件(名称)。 (它没有说明HEAD 提交中的副本。)或者:
git ls-files --modified
列出索引中的文件(名称)和与其在HEAD 提交中的副本不同(或根本不在HEAD 提交中)。没有选择:
git ls-files
列出索引中的文件(名称),不考虑 HEAD 提交或工作树中的文件。