0

想象一下,你有一棵树和一个文件。假设这个文件只有两种可能的状态a, 和b。如果它丢失或不存在,ø. 我正在尝试建立一个表格来了解所有可能的git-statuses。我相信我所说的是有道理的,但是,我已经标记了**有问题的领域:

head    index   working status
a       a       a       no changes**
a       a       b       unstaged:modified**
a       a       ø       unstaged:deleted**
a       b       a       staged:modified, unstaged:modified
a       b       b       staged:modified
a       b       ø       staged:modified, unstaged:deleted
a       ø       a       no changes**
a       ø       b       unstaged:modified**
a       ø       ø       staged:deleted**
ø       a       a       staged:new file
ø       a       b       staged:new file, unstaged: modified
ø       a       ø       staged:new file, unstaged: deleted
ø       ø       a       untracked

对于任何一个,*, ø, *我几乎觉得它取决于父树,以及它是否在索引中......例如,a, ø, ø就好像你已经从工作树中删除了 blob 以及索引。但是,从索引中删除是什么样的?是否只是将父树添加到暂存区域而删除了树条目?如果是这种情况,那么在索引中没有针对 blob 本身的条目是有道理的。

对于 index = head, ( a,a,a, a,a,b, a,a,ø) 的任何记录,我假设这种状态实际上不会发生,除非您正在使用管道命令。

如果您在我的表格中看到错误,和/或对此有所了解,那就太好了!提前致谢。

4

1 回答 1

1

将索引视为“建议的下一次提交”

基于提交的版本控制系统(例如 Mercurial 和 Git)需要一种方法来区分当前提交中的内容(与任何提交一样,永远无法更改)和我们进行的下一次提交中的内容,这当然必须在我们提交之前可以更改。Mercurial 基本上为此使用了工作树,但 Git 添加了一个称为index的额外层。然后,Git 能够为索引分配一些额外的属性:例如,当且仅当文件在索引中时,它才会被跟踪。在合并期间,索引具有额外的属性(我们将在这里忽略它们:-))。最后一个并发症我将留到最后。

但是,从索引中删除是什么样的?

从索引中删除文件相当于(从字面上看)从索引中删除文件。尝试运行git ls-files --stage以了解我的意思:对于您的第一行 ( a, a, a = no changes),您会发现在 index.html 中有一个名为的文件a。对于您的 row a, ø, a,该文件a根本不再在索引中(因此不会在您现在所做的新提交中)。

因此,将文件称为“暂存”可能有点误导。如果a根本不在索引中(但在 中HEAD),则文件“暂存以待删除”,但更简单地说“不在索引中”。一旦文件不在索引中,它也不会被跟踪,因此工作树版本成为未跟踪文件!

这意味着您的a, ø, b输入也是错误的:这里的文件被暂存以便删除,并且工作树变体b是一个未跟踪的文件。

a, a, ø条目可能是最难命名的。该文件仍在索引中,因此它将在您从此处开始的每次提交中,直到您将其从索引中删除。但是,该文件根本不在工作树中,因此您看不到它正在提交。如果您git add file在此状态下运行,Git 通过删除索引条目将不存在的工作树文件复制到索引中。

(Mercurial 也有类似的状态,因为有一个隐藏的内部数据结构称为清单,它的作用与 Git 的索引相同。如果文件从工作树中丢失,但在清单中,Mercurial 称文件丢失. Mercurial 尝试将工作树视为进入下一次提交的内容,因此您会认为如果文件像这样简单地消失了,它也应该从下一次提交中消失。根据文档,Mercurial 的行为方式是这样的原来,但发现这太容易出错了。)

用于探索的低级工具

  • 用于git ls-tree -r HEAD查看当前提交的整个树(如果只有一棵树您不需要-r)。
  • 用于git ls-files --stage查看当前索引的整个树:索引就像一棵扁平的树,如果您有一个dir以 filesd1和命名的子目录(子树),您将获得名为and的d2索引条目(与提交相比,顶部树将有一个名为的子树,并且该子树将有两个名为和) 的 blob。dir/d1dir/d2dird1d2
  • 使用操作系统的普通工具(ls例如)查看您的工作树。您的工作树本身对 没有什么意义git commit,它只是将现有索引(无论其中的任何内容)转换为一个或多个树对象以存储到新的提交中。git commit(您使用文件路径名参数或类似参数运行的所有更改-a。这里 Git 可能会将文件添加到索引中,甚至切换到它使用的临时备用索引,直到提交完成。这取决于在提供其他路径时是否,你使用--include--only。)

多了一道皱纹

因为 Git 拥有并公开了索引,它可以并且确实以两种不同的方式公开了另外一项功能。该索引的每个条目有两个标志位,称为假设不变跳过工作树。要查看这些标志位,git ls-files您必须添加--debug参数,但它们的作用可以相对简单地描述 - 事实证明有点过于简单 - 如下:

  • 如果在索引条目上设置了假设不变或跳过工作树标志,Git 应该在执行诸如git status.

  • 这可以大大加快 Git 的速度,但有一定的副作用。副作用可能是我们使用这些位的目的。

当你运行时git status,Git 运行两个 git diffs。一个比较HEAD与索引,第二个比较索引与工作树。第一个差异决定了git status --short输出的第一列,第二个差异决定了第二列。

假设不变和跳过工作树位告诉 Git 在第二个差异期间不要费心比较文件。1 请注意,要设置这些位,索引必须具有文件的条目,即,必须跟踪文件才能像这样跳过。我们大概可以假设索引条目匹配HEAD条目(如果不匹配,它将在下一次提交之后!),所以这些标志位的效果是我们永远不会看到文件被修改,并且git add通常会跳过文件同样:它不会将工作树版本复制回索引中。

我们的假设——索引条目与提交匹配——在某些极端情况下使我们误入歧途,这就是有两个位的原因。有关此的更多信息,请参阅Git - 'assume-unchanged' 和 'skip-worktree' 之间的区别


1第一个差异非常快,因为存储在提交或索引中时具有特殊的表单文件(blob)。具体来说,Git 可以通过比较它们的哈希 ID 来判断任何一个文件的内容是否与任何其他文件的内容匹配。如果哈希 ID 匹配,则文件相同;如果不是,则文件不同。Git 在这一点上并不是在寻找一个完整的差异,而只是一个--name-status风格差异:“文件是否相同?”

第二个差异要慢得多,因为在最坏的情况下,Git 必须打开并读取每个文件的全部内容。即使只是向文件系统询问文件(调用lstat系统调用)也比 Git 的内部 compare-hash-IDs 技巧慢得多。

于 2017-06-25T16:05:16.523 回答