各种git状态,对git状态(头部,索引,工作状态)的低级理解

时间:2017-06-25 14:28:55

标签: git version-control git-status git-index git-worktree

想象一下,你有一棵树有一个文件。假设此文件只有两种可能的状态:ab。如果它丢失或不存在,ø。我正在尝试构建一个表来理解所有可能的git-status es。我相信我所拥有的是有道理的,然而,我已经用**标记了问题:

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,aa,a,ba,a,ø)的任何记录,我假设除非您正在使用管道命令,否则实际上不会发生此状态。

如果你在我的桌子上看到错误,和/或任何光线都会很好!提前谢谢。

1 个答案:

答案 0 :(得分:1)

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

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

  

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

从索引中删除文件相当于(相当字面上)从索引中删除文件。尝试运行git ls-files --stage以查看我的意思:对于第一行(a, a, a = no changes),您会发现索引中有一个名为a的文件。对于您的行a, ø, a,文件a不再位于索引中(因此将在您现在制作的新提交中)。

结果,调用文件&#34;上演&#34;可能有点误导。如果a根本不在索引中(但在HEAD中),则该文件将被删除&#34;,但只需说出&#34更简单34;不在索引&#34;。一旦文件不在索引中,它也不会被跟踪,因此工作树版本将成为未经跟踪的文件!

这意味着您的a, ø, b条目也有误:此处文件已暂存,b的工作树变体是未跟踪的文件。

a, a, ø条目可能是最难命名的。该文件仍在索引中,因此它将在您从此向前进行的每个提交中,直到您从索引中删除它为止。但是,该文件根本不在工作树中,因此您无法看到它将进入提交状态。如果在此状态下运行git add file,Git会通过删除索引条目将工作树文件不存在复制到索引中。

(Mercurial具有类似的状态,因为隐藏的内部数据结构称为 manifest ,它扮演与Git索引相同的角色。如果文件丢失从工作树中,但是在清单中,Mercurial调用文件缺少 .Mercurial尝试将工作树视为进入下一次提交的内容,所以你会认为如果文件像这样简单地消失了,它也应该从下一次提交中消失。根据文档,Mercurial最初表现得这样,但发现这很容易出错。)

用于探索的低级工具

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

再皱一次

因为Git拥有并公开了索引,所以它可以并且确实以两种不同的方式公开了另一个功能。索引每个条目有两个标志位,称为假设未更改 skip-worktree 。要使用--only查看这些标志位,您必须添加git ls-files参数,但它们的作用可以相对简单地描述 - 有点过于简单,结果如下:

  • 如果在索引条目上设置了assume-unchanged或skip-worktree标志,Git应该只是关闭它的眼睛&#34;在执行--debug等操作时,工作树中的内容。

  • 这可以加速Git,但有一定的副作用。副作用可能是我们使用的比特。

当您运行git status时,Git会运行两个 git status。一个比较git diff与索引,第二个比较索引与工作树。它是确定HEAD输出的第一列的第一个差异,以及决定第二列的第二个差异。

假设未更改和跳过工作树位告诉Git在第二个差异期间不要打扰比较文件。 1 请注意,对于要设置的这些位,索引必须有一个条目用于文件,即文件必须跟踪,以便像这样被跳过。我们可以假设索引条目与git status --short条目匹配(如果它没有,它将在下一次提交之后!),因此这些标志位的效果是我们从未将文件视为已修改,HEAD通常也会跳过文件:它不会将工作树版本复制回索引。

我们的假设 - 索引条目与提交相匹配 - 导致我们在某些极端情况下误入歧途,并且是两个位的原因。有关详细信息,请参阅Git - Difference Between 'assume-unchanged' and 'skip-worktree'

1 第一个差异非常快,因为特殊的表单文件(blob)存储在提交或索引中时。具体来说,Git可以通过比较它们的哈希ID来判断任何一个文件的内容是否与任何其他文件的内容匹配。如果哈希ID匹配,则文件相同;如果没有,文件是不同的。 Git目前并不是在寻找完整的差异,而只是git add样式差异:&#34;文件是否相同?&#34;

第二个差异要慢得多,因为在最坏的情况下,Git必须打开并读取每个文件的全部内容。即使只是询问文件系统关于该文件(调用--name-status系统调用)也比Git的内部比较哈希ID技巧慢得多。