0

首先,让我澄清一件事:尽管有很多关于撤消 git checkout 的问题,但这不是(至少据我评估)一个重复的问题。

现在让我解释一下我的用例:我正在使用 sparse-checkout 功能来拥有一个工作副本,其中不包含中央远程仓库中的所有文件。

现在假设我想将一个文件添加到我的工作副本中,但我犯了一个错误并签出了错误的文件。

我想恢复我的工作副本,就好像该文件从未签出一样。

那就是:我想从我的工作副本中删除该文件,但我希望从远程仓库中删除该文件。我一直在到处寻找,但仍然没有找到一种方法来做我想做的事。

4

1 回答 1

1

你真的不需要做任何事情。您可以做一些事情,但这不是必需的,如果您意外提取的文件没有造成任何问题,您可能应该将其留在那里。

这可能需要一点解释。

我正在使用 sparse-checkout 功能来拥有一个工作副本,其中不包含中央远程仓库中的所有文件。

虽然您的工作副本可以省略一些文件,但您的存储库不能省略这些文件。所以你已经有了它们。稀疏结帐选项所做的唯一一件事就是防止它们出现在您的工作树中。

您可能已经知道这一点,但让我们回顾一下有关 Git 的一些项目,以确保我们有一个共享的词汇表:

  • Git存储库本质上由两个数据库组成。(通常更大)主数据库保存提交和其他支持 Git 对象。第二个,通常更小,数据库保存名称——分支名称、标签名称和其他此类名称——并且对于每个名称,都有一个对应的对象哈希 ID。对于分支名称,这些哈希 ID 始终是提交哈希 ID;其他名称有时可以保存一些其他内部 Git 对象的哈希 ID。

    两个数据库都是简单的键值存储。每个都有一个特定的 Git 特定实现,尽管现成的数据库可以工作(尽管使用和管理它会更慢更难,或者至少,这是使用私有数据库的借口)。

    主数据库中的所有对象(包括所有提交)都是完全只读的。这是因为密钥是哈希 ID,而哈希 ID 是对内容(存储在该密钥下的值)应用加密校验和算法的结果。Git 在提取内容时会进行验证:内容必须散列回密钥。这会检测(但无法纠正)任何数据库损坏。

  • 那么,提交是主数据库中的对象。它们有两个部分:一个快照(所有文件的快照,按照这些文件在制作快照时的形式)和一些元数据。我们将在这里跳过所有细节,因为它们无关紧要,但这样做的效果是每个提交都存储每个文件。这包括您故意没有通过稀疏签出签出的文件。

  • Git从 Git 所谓的索引暂存区缓存中进行新的提交。最后一个术语现在很少见,主要出现在各种 Git 命令的标志参数中。这三个名称描述了 Git 用于多种用途的中间数据结构:--cached

    • 密切关注您的工作树(缓存方面),以及
    • 存储建议的下一个快照的文件名和模式(暂存区方面)。

    在冲突合并期间扩展索引时会出现第三个目的,但我们将在此处跳过它,因为这与手头的问题无关。

  • 最后,在您的工作树中,Git 从提交中提取文件。通常 Git从提交中提取所有文件。这里的实际做法是 Git 首先将所有文件复制到 Git 的索引中。这会为缓存部分创建空间,并创建名称和模式部分并存储一个blob 对象哈希 ID以表示文件的实际内容。

Git 需要这个索引来保存提交中的所有文件,即使使用稀疏检出也是如此。所以 Git 的索引总是保存每个文件。这占用的空间相对较小,因为实际内容作为 blob 对象存储在大数据库中。但是,如果您使用稀疏检出,Git 会将每个索引条目文件扩展为一个工作树副本,该副本是一个实际的、可读和可写的文件,而不仅仅是数据库中的一些内部 blob 对象。

我们需要真实的文件来完成任何实际的工作。如果我们需要做的只是保留文件以供使用git diff并进入新的提交等,并且我们不必实际读取和写入它们,我们可以将它们保留为内部 blob 对象,这就是 Git 所做的所有签出的提交。

因此,这就是稀疏结帐进入图片的地方。我们只是告诉 Git:哦,顺便说一下,当你开始从索引中提取所有文件时,跳过其中的一些。 为了告诉 Git,在索引和工作树之间的低级接口处,我们让 Git 在缓存数据中设置了一位。该位称为skip-worktree位,我们可以通过以下方式显式设置或清除它:

git update-index --skip-worktree path/to/file

或者:

git update-index --no-skip-worktree path/to/file

请注意,这对大数据库中的任何实际存储对象都没有影响,并且对我们的工作树(或不在我们的工作树)中的任何文件都没有实际影响。它只是设置或清除索引条目上的位。为此,必须存在索引条目。

然后,我们可以通过以下方式实现稀疏结帐:

  • 选择一个提交;
  • 将该提交读入索引,但尚未创建工作树;
  • 设置我们喜欢的所有跳过工作树位;和
  • 签出我们工作树的索引。

Git 中有一些低级命令可以做到这一点。我们之所以拥有稀疏检出功能,而不是使用那些低级命令,是因为对每个文件都这样做是一件非常痛苦的事情。所以稀疏检出功能只是自动git checkout执行此操作我们告诉 Git 哪些文件应该出现在我们的工作树中,哪些文件应该进入 Git 的索引但设置了 skip-worktree 位。

现在让我们回过头来git commit记下它是如何工作的。当我们运行时git commit,我们是在告诉 Git 进行新的提交。 Git 目前不使用我们的工作树。 我们可以git status先运行并获取列表,或者我们可以让git commit运行git status(默认情况下会这样做:如果我们不希望这样做,我们必须显式地禁止它)并用结果填充我们的提交消息模板,但是一种或另一种方式,提交不会我们的工作树中提交。1 它来自索引——它已经保存了每个文件,包括那些提取到我们工作树中的文件。

这意味着当您使用稀疏结帐时,您仍然可以使用每个文件。只是所有文件都在 Git 的索引中,您(和程序)无法在其中查看或更改它们。您的工作树省略了某些文件的扩展正常文件形式,因此您无法查看或更改它们。它保存其他文件的扩展、普通文件形式,以便您可以查看和更改它们——但如果您确实更改了它们,您仍然需要运行git add将它们复制回索引中。2 毕竟,Git 将根据索引中的内容构建下一个提交,而不是工作树中的内容!

考虑这一点的一个好方法是索引保存您提议的下一次提交。由于索引包含所有文件(取自当前提交),因此工作树中的内容无关紧要。 这就是为什么你不需要做任何事情。 您可以将工作树文件留在那里,即使您不打算对其进行任何操作。只要它在 Git 的索引中,它就会出现在新的提交中,无论它是否在你的工作树中。所以不要打扰删除它。


1当使用git commit --onlyor git commit --includewith pathspecs 时,提交代码首先创建一个额外的临时索引,然后更新临时索引,就像 via 一样git add,然后从临时索引进行新的提交。然后,当且仅当提交成功时,它才会调整实际索引。我们将跳过所有这些细节,但请注意,即使在这些模式下,提交也是从索引构建的。只是Git没有使用“the”索引,而是使用了临时辅助索引。

2这并不重要,但该git add步骤的工作原理是将工作树副本压缩回内部 Git 对象,从而生成一个 blob 哈希 ID。这会立即针对任何现有的匹配 blob 自动进行重复数据删除,因此只有在以前从未见过内容的情况下,存储库数据库才会增长。然后 Git 将哈希 ID 填充到索引中,以便现在更新索引。


如果工作树文件挡住了你的路怎么办?

假设工作树文件太大了,它会填满一个小的(SSD?)驱动器。你不需要它,它就路上。你怎么能现在从你的稀疏结帐中删除它,而不是从未来的提交中删除它?

如果你仔细阅读上面的机制描述,答案是显而易见的——至少是高层次的答案;Git 命令集可能仍然有点晦涩(尽管我确实提到了它们)。您只需从工作树中删除该文件的副本。这部分非常简单。您不需要任何特殊命令。用于删除文件的常规日常计算机命令(无论是该文件rm还是DEL其他文件)都有效,因为您的工作树是常规的日常文件集。所以只是rm bigfile或其他。

然而,一旦你这样做了,git status就会开始抱怨它:它会说文件的工作树副本已经消失了。更糟糕的是,一揽子git add操作可能会删除索引副本,3所以从现在开始,您可能需要小心使用git add命令。这是您要使用 Git 命令的地方:

git update-index --skip-worktree bigfile

这设置了我之前提到的稀疏结帐代码使用的跳过工作树位。skip-worktree 位只是告诉各种 Git 命令,包括git status和一揽子 en-massegit add命令,应该完全忽略工作树副本或缺少工作树副本。只需将索引中的任何内容保留在索引中即可。

因此,这两个命令——每天的“删除文件”一个和git update-index一个带有--skip-worktree标志的命令——足以从你的工作树中删除文件,而不会影响 Git 索引中的副本。索引副本将按原样进入未来的提交。请记住,提交是重复文件删除,所以这只是重新使用早期提交的副本,基本上不占用空间。

因此,您可以选择:什么都不做(因为什么都不需要做),或者在不使用 Git 命令的情况下删除文件,如果git status遇到抱怨,请设置 skip-worktree 位。


3git add为了使这有意义,可以将某些文件的索引副本与该文件的工作树副本相匹配。如果工作树副本已被删除,这将删除索引条目。

于 2020-12-03T23:10:38.623 回答