“git ls-files”到底是做什么的?我们如何从它中删除一个文件?

ippsafx7  于 2023-10-14  发布在  Git
关注(0)|答案(5)|浏览(206)

它是否显示来自本地存储库、暂存存储库、远程存储库或其他地方的文件?
我经常看到一个文件出现在“git ls-files”中。该文件已从远程存储库中删除。之后,我尝试做一个git pull。但是,该文件仍然显示在此命令列表中。它不应该出现在这里,因为它也不存在于远程存储库中。

dgtucam1

dgtucam11#

摘要

你需要明白Git至少会存储 * 三个,有时候会多达五个 * 文件的活动副本:一个在 * 当前提交 * 中,一个(或两个或三个!),以及 * 工作树 * 中的一个--您唯一可以看到并使用的一个。git ls-files命令查看这些副本,然后根据您提供给git ls-files的标志,告诉您有关其中一些副本的信息。
如果没有每个文件三到五个副本的想法,Git中的许多东西将永远没有意义。(好吧,有些事情即使用它也很棘手,但这完全是另一个问题。)😀的方式

我认为这里有两个问题。一个需要一些术语,然后另一个应该到位:
[git ls-files]是否显示本地存储库中的文件,
算是吧,但是:
暂存库,
Git没有staging repository。在不同的Git文档中,每个仓库都有一个叫做 indexstaging area 的东西。(还有一个废弃的第三个名称,cache,也出现在the Git glossary中。
远程存储库
绝对不是:根本不需要有任何远程仓库,也就是说,其他Git有自己的仓库,如果有的话,只有git fetchgit push让你的Git调用他们的Git并与他们交换数据。(git ls-remote执行git fetch的前一小部分,git pullrunsgit fetch,因此这两个也与远程交换数据。但git ls-files并没有。)
还是从别的地方
是的,算是吧。这让我们回到第一部分。让我们来看看the Git glossary中定义的这三个术语。下面的斜体(包括粗斜体)文本直接来自链接文档:

    • 存储库 *
  • 参考文献的集合以及包含从参考文献可访问的所有对象的对象数据库,可能伴随着来自一个或多个瓷器的Meta数据。仓库可以通过替代机制与其他仓库共享对象数据库。*(所有链接都是他们的)

这当然充满了更多的术语。为了让它变得更加神秘,他们在这里说的是,仓库本身 * 不 * 包括索引和工作树:它主要由 commits(及其内容)组成。当然,这需要我们定义“index”和“work-tree”,所以让我们继续:

    • 索引 *
  • 带有stat信息的文件集合,其内容存储为对象。索引是工作树的存储版本。说实话,它还可以包含第二个,甚至第三个版本的工作树,在合并时使用。
    • 工作树 *(我通常称之为工作树):
  • 实际 checkout 文件的树。工作树通常包含HEAD提交树的内容,以及您已经做过但尚未提交的任何本地更改。*

提交被永久冻结

当你运行git commit时,Git会对你所有的文件(当然,是所有被跟踪的文件)做一个快照,并把它和一些元数据(比如你的名字和电子邮件地址)存储在一个提交中。这个提交大多是永久性的你可以 * 摆脱提交,通常有相当的难度,但是为了遵守,就把它们看作是永久性的并且是完全的,完全的,100%只读。它是故意只读的,因为这允许其他提交 * 共享 * 相同的文件副本,所以如果你提交同一个文件一次,十次,甚至一百万次,仓库中实际上只有 * 一个 * 该文件的副本。只有当你将文件更改为新版本时,Git才需要提交一个新的单独副本。
提交是有编号的,但不是通过一个简单的顺序编号系统。也就是说,我们可以把它们画成一系列简单的数字或字母:

... <-C4 <-C5 <-C6 ...

其中每个后来的提交都指向其直接的前一个提交。但他们的真名是很大很丑的哈希ID。每一个都是独一无二的,这就是为什么它们必须如此之大、丑陋和随机。每个hash ID实际上是一个加密校验和,根据提交的内容计算,这样宇宙中的每个Git都会同意 * 那个 * 提交,并且 * 只有 * 那个提交,得到 * 那个 * 校验和。这是你-甚至Git-不能改变它的另一个原因:如果你从仓库数据库中取出一个提交,修改它,甚至修改 * 一个比特 *,然后把它放回数据库,你得到的是一个 * 新的 * 提交,带有一个新的不同的哈希ID。
所以提交被完全冻结,永远。它们里面的文件也被永久冻结,并被压缩,并且是一种特殊的Git格式。我喜欢称这些文件为“冻干”。这意味着,嘿,它们非常适合存档,但是对于完成任何新的工作来说,它们都是完全无用的。这就意味着Git必须提供某种方式来获取这些冻干的文件,并将它们重新水合为有用的形式。

工作树提供有用的表单副本

事情没有比这更简单的了:工作树有有用的形式,你的文件的复水副本。因为它们只是计算机上的普通日常文件,所以您可以查看它们,使用它们,随意更改它们,或者使用它们。从技术上讲,它们根本不在存储库中--它们更像是就在它旁边。在典型的设置中,存储库本身位于工作树顶层的.git目录/文件夹中。
显然,如果有一个提交被提取出来用于 make 工作树,那么现在每个文件都必须有两个副本:一个是冷冻干燥的,另一个是正常工作的Git可以在这里停下来。Mercurial does 停止在这里:如果你使用Mercurial而不是Git,你不需要担心第三个副本,因为没有第三个副本。但是Git继续存储更多的文件副本。

index / staging-area位于commit和work-tree之间

Git在这里所做的是为每个文件创建一个 * 第三 * 副本,位于冻干的提交副本和工作树副本之间。这第三个副本是提交文件格式的--也就是说,预脱水的--但由于没有在 commit 中,它实际上并没有完全冻结:它可以在任何时候被替换。这就是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 revertgit 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会说你有为提交而暂存的更改,因为索引副本与当前提交副本不同,* 和 * 说你有为提交而暂存的更改,因为工作树副本与索引副本不同。

工作树可以包含索引中根本没有的文件

索引最初只是当前提交的副本。然后Git也会将这些文件复制到工作树中,以便您可以使用它们。但是你可以在工作树中创建文件,而不是在上面运行git add。这些文件现在不在索引中,如果你运行git commit,它们也不会出现在新的提交中,因为Git会从索引构建新的提交。
你也可以从索引中 * 删除 * 文件 *,而不从工作树中删除它们:

git rm --cached file.ext

删除索引副本。当然,它不能触及当前提交的冻结副本,但是如果你现在做一个 new 提交,新的提交中根本不会有file.ext。(当然,前一个提交仍然有效。)
任何文件,* 是 * 在您的工作树 * 现在 *,是 * 不是 * 在您的索引 * 现在 *,是一个 * 未跟踪 * 文件。它的不可追踪性来自于它不在你的索引中。将该文件放入您的索引中,无论您如何将其放入索引,它都会被跟踪。从你的索引中删除它,它就不被跟踪了,不管你是如何从索引中删除它的。这是索引的最后一个作用:以确定哪些文件被 * 跟踪 *,因此将在下一次提交。

现在我们可以清楚地看到git ls-files的作用了

git ls-files所做的是读取所有内容:提交、索引、* 和 * 工作树。根据你给予 togit ls-files的参数,它会打印索引和/或工作树中的部分或全部文件的名称:

git ls-files --stage

列出了索引/暂存区域中的文件及其暂存插槽编号,沿着。(它没有说明HEAD提交和工作树中的副本。)或者:

git ls-files --others

列出工作树中但索引中没有的文件(名称)。(它没有说明HEAD提交中的副本。)或者:

git ls-files --modified

列出了索引 * 和 * 中的文件(名称)与HEAD提交中的副本不同(或根本不在HEAD提交中)。没有选择:

git ls-files

列出了索引中的文件(名称),而不考虑HEAD提交或工作树中的文件。

knsnq2tg

knsnq2tg2#

git ls-files在您的情况下工作正常。由于git status显示X文件已从工作目录中删除,这意味着该文件仍然存在于索引中。这就是git ls-files显示X的原因,因为该命令显示索引的内容。
现在,你必须从索引中删除该文件,只需运行:

git rm --cached <pathToXFile>
blpfk2vs

blpfk2vs3#

只是想分享:
参考已接受的答案https://stackoverflow.com/a/56242906/2623045和与https://stackoverflow.com/users/1256452/torek的对话:
如果问题是,如果我 checkout 了一个特殊的提交,我如何找到应该在那里的文件/对象,另一个答案可能是这样的:

git ls-tree -r -l HEAD

Torek还提到“(HEAD可能是对一个不存在的分支名称的符号引用)”,但我现在不明白。
更一般的是:

git ls-tree -r -l commit-hash

这也适用于使用开关-n克隆的存储库(无 checkout )
只是想知道输出的魔力在哪里记录

从使用以下命令克隆的存储库中提取:git clone -n https://github.com/nvie/gitflow.git

100755 blob fd16d5168d671b8f9a8a8a6a140d3f7b5dacdccd    git-flow
100644 blob 55198ad82cbfe7249951aa75f1373a476997d33a    git-flow-feature
100644 blob ba485f6fe4b7d9c35bc01d2a6bd4ae201bccc9bd    git-flow-hotfix
100644 blob 5b4e7e807423279d5983c28b16307e40dfdb51d7    git-flow-init
100644 blob cb95bd486deb7089939362705d78b2197893f578    git-flow-release
100644 blob cdbfc717c0f1eb9e653a4d10d7c4df261ed40eab    git-flow-support
100644 blob 8c314996c0ac31f1396c48af5c6511124002dab7    git-flow-version
100644 blob 33274053347f4eec2f27dd8bceca967b89ae02d5    gitflow-common
120000 blob 7b736c183c7f6400b20ea613183d74a55ead78b5    gitflow-shFlags
160000 commit 2fb06af13de884e9680f14a00c82e52a67c867f1  shFlags

我的解释是:
哈希似乎是“blob校验和”(没有提交哈希)。如果一次提交中有多个文件,则相同的校验和可以出现多次。最后三次轻咬例如。100644看起来像八进制编号方案(rw-r--r--)中的Linux文件访问属性。如果对象不是常规文件,则前三个半字节不是100。在真实的生活中,gitflow-shFlags是一个符号链接,而shflags是一个子模块目录。
编辑:刚刚被https://github.com/git/git/blob/master/Documentation/technical/index-format.txt绊倒(谷歌:git --index-info,STACKOVERFLOW:What does the git index contain EXACTLY?

32-bit mode, split into (high to low bits)

  4-bit object type
  valid values in binary are 1000 (regular file), 1010 (symbolic link)
  and 1110 (gitlink)

  3-bit unused

  9-bit unix permission. Only 0755 and 0644 are valid for regular files.
  Symbolic links and gitlinks have value 0 in this field.

所以如果你把半字节解释为八进制值
100644:**1 '000'**000'110' 100 '100 -->对象类型为常规文件
120000:**1 '010'**000 '000' 000 '000 -->对象类型为符号链接
160000:**1 '110'**000 '000' 000 '000 -->对象类型为gitlink
OMG:为什么直接从git手册页中提取这些信息如此困难?
下一个问题:什么是“gitlink”?它只与git子模块关联吗?

yqyhoc1h

yqyhoc1h4#

在Git 2.35(Q1 2022)中,“git ls-files“学习了“--sparse“选项来帮助调试。
它与sparse index, after a git sparse checkout command一起使用。
参见commit 408c51fcommit c2a2940commit 3a9a6accommit 7808709commit 5a4e054(2021年12月22日)by Derrick Stolee ( derrickstolee )
(由Junio C Hamano -- gitster --合并至commit 3c0e417,2022年1月10日)

ls-files:add --sparse选项

签字人:德里克·斯托里
git ls-filesman)”的现有调用方需要文件名,而不是目录。在这种情况下,最好展开稀疏索引以显示包含的所有文件。
但是,Maven用户可能希望检查索引本身的内容,包括哪些目录是稀疏的。
添加--sparse选项以允许用户请求此信息。
在测试过程中,我注意到,当有问题的文件不在稀疏 checkout 定义范围内时,诸如--modified之类的选项不会影响输出。
git ls-files现在在其手册页中包括:

--sparse

如果索引是稀疏的,则显示稀疏目录,而不展开到包含的文件。
稀疏目录将以一个尾随斜杠显示,例如“x/“表示稀疏目录“x“。

qqrboqgw

qqrboqgw5#

我经常看到一个文件出现在“git ls-files”中。该文件已从远程存储库中删除。之后,我尝试做一个git pull。
您将该文件添加到索引中,但尚未提交或删除它,因此Git会为您保存它,直到您决定如何处理它。
如果你不想让它出现在你的索引中,就把它删除。通常是git rm --cached,或者如果您也希望它从工作树中消失,则只需git rm
当你工作的时候,你经常会发现一些愚蠢的小错误,需要修复,但实际上不是你当前任务的一部分。Git让处理这样的事情变得非常简单:从你的维护库中 checkout 一个bug修复分支,提交那个修复,回到你正在做的事情并合并那个修复。
如果可能的话(这通常是微不足道的,Git只是默默地做了这件事),Git在做这件事的时候丝毫不干扰你在飞行中所做的任何其他更改。
你会发现Git处理动态工作的方式避免了无用的流失,重要的是,这就是Git处理动态工作的方式:它会一直保存在索引中,直到你决定如何处理它。只要你不告诉Git在那里放其他东西,Git就会默默地携带你添加的东西。

相关问题