【问题标题】:What does "git ls-files" do exactly and how do we remove a file from it?“git ls-files”究竟做了什么,我们如何从中删除文件?
【发布时间】:2019-10-07 16:06:12
【问题描述】:

它是否显示来自本地存储库、临时存储库、远程存储库或其他地方的文件?

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

【问题讨论】:

  • 您的git status 显示什么?
  • @NghiaBui 它以红色显示文件 X 已被删除(这意味着它正在被跟踪)。 X 是不应出现在“git ls-files”中的文件。此文件在远程存储库中不存在。我试着做一个 git pull + git fetch + git reset --hard origin/branch_name 。这些都没有解决问题。
  • git help ls-files 说 > git-ls-files - Show information about files in the index and the working tree
  • @jthill 是的。我已经阅读了,但仍然不清楚他们是在谈论本地存储库、暂存存储库还是本地文件列表。还不清楚如何删除显示在此列表中但不在本地或远程存储库中的文件。

标签: git


【解决方案1】:

总结

您需要理解 Git 存储每个文件的至少三个,有时最多五个活动副本的想法:一个在当前提交中, index 中的一个(或两个或三个!),以及一个 - 您可以看到和使用的唯一一个 - 在您的 work-tree 中。 git ls-files 命令查看这些副本,然后根据您提供给 git ls-files 的标志告诉您其中的一些内容。

如果没有每个文件 3 到 5 个副本的想法,Git 中的很多东西都将毫无意义。 (嗯,即使 使用它,有些事情仍然很棘手,但这完全是另一个问题。?)

我认为这里有两个问题。一个需要一些术语,然后另一个应该到位:

[git ls-files] 是否显示本地存储库中的文件,

有点,但是:

临时存储库,

Git 没有暂存存储库。在不同的 Git 文档中,每个存储库都有被称为 indexstaging area 的东西。 (还有一个已过时的第三个名称,cache,它也出现在 the Git glossary 中。)

远程仓库

绝对不是:根本不需要任何远程存储库(即其他 Git 拥有自己的存储库),如果有的话,只有 git fetchgit push 让您的 Git 调用他们的 Git 并与之交换数据他们。 (好吧,git ls-remote 执行git fetch 的第一个点,git pull runs git fetch,所以这两个也与遥控器交换数据。但git ls-files 没有。 )

还是来自其他地方?

是的,有点。这让我们回到了第一部分。因此,让我们采用the Git glossary 中定义的这三个术语。下面的斜体(包括粗斜体)文本直接来自链接文档:

  • 存储库

    refsobject database 的集合,其中包含来自引用的 reachable 的所有对象,可能伴随有来自一个或多个 porcelains 的元数据。存储库可以通过alternates mechanism 与其他存储库共享对象数据库。(所有链接都是他们的)

    这当然充满了更多的术语。为了稍微揭开它的神秘面纱,他们在这里所说的是存储库本身包含索引和工作树:它主要由 commits(及其内容)。当然,这需要我们定义“索引”和“工作树”,所以让我们继续:

  • 索引

    具有统计信息的文件集合,其内容存储为对象。该索引是您的working tree 的存储版本。说实话,它还可以包含第二个,甚至第三个版本的工作树,在merging 时使用。

  • 工作树(我通常称之为工作树):

    实际签出文件的树。工作树通常包含HEAD 提交树的内容,以及您已进行但尚未提交的任何本地更改。

提交被永久冻结

当您运行 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 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 会说你有更改暂存等待提交,因为索引副本与当前提交副本不同,并且说你有更改没有 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 提交或工作树中的文件。

【讨论】:

  • 我不确定index 是否包含文件的副本,而是index 包含the name (40 chars long sha1),可以在.git/objects 文件夹中找到。 100644 802992c4220de19a90767f3000a79a31b98d0df7 0 README.md 上面的这一行是从index 中提取的,这不是文件README.md 的副本。这只是工作树中文件的名称,哈希值为keygit 使用它在objects 文件夹中查找blob
  • @IvanRuski:是的,索引包含哈希名称和对内容的引用。但是本地文件系统中的文件可能是一个名称和对内容的引用。然后您是否说“我的目录中没有任何文件,它只有文件名”? :-) 这在技术上是正确的——但它并没有完成任何工作。有时知道是有用的,但大多数情况下,我们只是说我们的目录中有文件。
  • 感谢您的出色回答。我已经分别了解这些主题,但您的帖子确实将它们联系在一起!
  • @grenix: git ls-files 将显示 Git 索引中的文件(如果有)。如果您运行git clone -n(无结帐),则索引将为空,因此不会显示任何内容。否则,它们将是 Git 在结帐期间推送到其索引中的文件,这将是出现在您的工作树中的同一组文件,是的。请注意,您可以在结帐后删除部分或全部这些工作树文件,而不会影响索引副本。 Git 会有点抱怨,但你仍然可以进行包含所有文件的新提交!
  • @grenix:是的,不过记得检查错误情况(HEAD 可能是对不存在的分支名称的符号引用)。通过HEAD 读取提交哈希ID,然后检查相应的树对象。在 shell 中,您可以使用 git ls-tree -r HEAD 来执行此操作。 Git 可以创建符号链接但通常不会创建 empty 目录;空目录案例仅适用于 gitlinks(子模块部分)。
【解决方案2】:

git ls-files 在您的情况下可以正常工作。由于您的git status 显示 X 文件已从工作目录中删除,这意味着该文件仍存在于索引中。这就是git ls-files 显示 X 的原因,因为该命令显示了索引的内容。

现在,您必须从索引中删除该文件,只需运行:

git rm --cached <pathToXFile>

【讨论】:

  • 我已经在本地删除了文件并尝试将更改推送到 git。 git add(删除的文件)。 git commit -m (关于删除它的一些消息)。 git push(由于服务器问题而失败)。现在向后移植我必须使用“git reset HEAD^ --soft(保存您的更改,返回到最后一次提交)(stackoverflow.com/a/10169389/169513)。在此处添加此评论,以便帮助其他可能被困在这一点上的人.
【解决方案3】:

只是想分享:

参考接受的答案 https://stackoverflow.com/a/56242906/2623045 并与讨论 https://stackoverflow.com/users/1256452/torek:

如果问题是,如果我检查了一个特殊的提交,我如何找出应该存在哪些文件/对象,另一个答案可能是这样的:

git ls-tree -r -l HEAD

Torek 还提到“(HEAD 可能是对不存在的分支名称的象征性引用)”,但我暂时不明白这一点。

所以更笼统:

git ls-tree -r -l commit-hash

这也适用于使用 switch -n 克隆的存储库(不签出)

只是想知道输出的魔力记录在哪里

从使用以下代码克隆的 repo 中提取: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 看起来像 linux 文件访问属性 (rw-r--r--)。如果对象不是常规文件,则前三个半字节不是 100。在现实生活中 gitflow-shFlags 是一个符号链接,并且 shflags 是一个子模块目录。

编辑: 刚刚偶然发现https://github.com/git/git/blob/master/Documentation/technical/index-format.txt(GOOGLE: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 子模块相关联?

【讨论】:

  • 这些模式是一个谜,除非或直到你注意到它们是从 Linux/Unix“inode”模式派生的。 gitlink 模式比较特殊,确实是针对子模块的。
【解决方案4】:

我经常看到“git ls-files”中存在的文件。该文件已从远程存储库中删除。之后我尝试做一个 git pull。

您将该文件添加到您的索引中,但尚未提交或删除它,因此 Git 会为您保留它,直到您决定如何处理它。

如果您不希望它出现在索引中,请将其删除。通常是git rm --cached,或者如果您还希望它从您的工作树中消失,只需git rm

在您工作时,您经常会发现一些愚蠢的小错误需要修复,但实际上并不是您当前任务的一部分。 Git 让处理这样的事情变得非常容易:从你的维护库中检查一个错误修复分支,提交那个修复,回到你正在做的事情并合并那个修复。

如果可能的话(而且它通常是那么微不足道,Git 只是默默地做到了)Git 做到了这一点,丝毫不会干扰您在飞行中所做的任何其他更改。

您会发现其他情况下,Git 处理动态工作的方式避免了无用的流失,重要的是,这就是 Git 处理动态工作的方式:它保留在索引中,直到您决定如何处理它.只要你不告诉 Git 把其他东西放在那里,Git 就会默默地携带你添加的东西。

【讨论】:

    【解决方案5】:

    使用 Git 2.35(2022 年第一季度),“git ls-files”学习“--sparse”选项来帮助调试。

    它与sparse index, after a git sparse checkout command一起使用。

    commit 408c51fcommit c2a2940commit 3a9a6accommit 7808709commit 5a4e054(2021 年 12 月 22 日)Derrick Stolee (derrickstolee)
    (由Junio C Hamano -- gitster -- 合并到commit 3c0e417, 2022 年 1 月 10 日)

    ls-files:添加 --sparse 选项

    签字人:Derrick Stolee

    git ls-files(man)”的现有调用者需要文件名,而不是目录。在这种情况下,最好扩展一个稀疏索引以显示所有包含的文件。

    但是,专家用户可能希望检查索引本身的内容,包括哪些目录是稀疏的。
    添加--sparse 选项以允许用户请求此信息。

    在测试过程中,我注意到--modified 等选项在相关文件超出 sparse-checkout 定义时不会影响输出。

    git ls-files 现在包含在其man page 中:

    --sparse

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-08-06
      • 1970-01-01
      • 2016-08-13
      • 2016-07-18
      • 2019-11-09
      • 2012-07-23
      • 2016-09-10
      • 2023-03-15
      相关资源
      最近更新 更多