【问题标题】:understanding git reset effect on index了解 git reset 对索引的影响
【发布时间】:2019-05-03 23:54:16
【问题描述】:

在阅读有关 git reset 的文档/教程时,我遇到了一个小冲突:例如,对于 git reset --mixed,文档说:

reset 的下一件事是用HEAD 现在指向的任何快照的内容更新索引

导致我的冲突的原因是我期望清除索引而不是更新索引。是否使用HEAD 现在指向的任何快照清除或更新了索引?

【问题讨论】:

标签: git git-reset git-index


【解决方案1】:

TL;DR

索引总是更新的。该索引保存您打算进行的下一次提交,因此它永远不会为空。 (什么,从来没有?嗯,几乎从来没有:它在你刚刚创建的 new 存储库中是空的,它没有文件,如果你现在运行git commit,它也不会提交任何内容。如果你git rm 一切。)

您在这里的困惑几乎肯定与the comment PetSerAl made 有关。那些刚接触 Git 的人经常被告知或展示,或者至少导致他们相信提交和/或 Git 的索引包含 更改,但这是错误的!一旦你摆脱了这种不正确的信念,Git 的一些奥秘就开始变得更有意义了。 (不是所有 Git 对任何人都有意义,即使是我。? 所以如果grokGit 需要很长时间,请不要担心。)

在 Git 中,提交包含所有文件的完整快照。它还包含一些元数据——关于提交本身的信息,例如您的姓名、电子邮件地址和时间戳。元数据中包含提交的 parent 提交的哈希 ID - 或者,对于合并提交,多个父级,复数 - 通过 comparing 提交到其父级,Git显示你的变化。每个提交都有自己唯一的哈希 ID,例如 8858448bb49332d353febc078ce4a3abcc962efe(这是 Git 存储库中 Git 的提交的 ID)。该提交是一个快照,但该提交有一个父级(在本例中为 67f673aa4a...),因此 Git 可以通过提取 both 较早的 @ 来显示8858448bb4... 987654330@ 8858448bb4,然后比较两者。 git show 命令就是这样做的,因此您看到的是8858448bb4 中的更改,而不是8858448bb4 中的

(这就像告诉你今天比昨天暖或冷 5 度,或多或少有风,而不是把天气作为一堆数字给出。数据库存储绝对值,但大多数情况下我们想知道它是否更好出来。)

索引存储您可以进行的下一次提交

您可以通过各种方式查看 Git 的提交,当然还可以使用它们的哈希 ID 来命名它们,就像我在上面所做的那样。你可以直接看到你的工作树——Git 允许你在其中查看和编辑你的文件——它们在你的计算机上,以它们正常的日常形式存在。但是您不能查看很好的索引。这有点看不见。这是一个问题,因为它也很关键。

大多数版本控制系统根本没有索引,或者如果它们有类似的东西,请将其隐藏得很好,以至于您永远不必知道它。但是 Git 做了一件奇怪的事情,它强迫你理解 Git 的索引,同时也把它隐藏了一点。

如果您现在确实想查看索引中的文件列表,可以使用git ls-files

$ git ls-files | head
.clang-format
.editorconfig
.gitattributes
.github/CONTRIBUTING.md
.github/PULL_REQUEST_TEMPLATE.md
.gitignore
.gitmodules
.mailmap
.travis.yml
.tsan-suppressions
$ git ls-files | wc -l
    3454

索引中有近 3500 个文件,在这个 Git 存储库中。那是很多文件!这就是 为什么Git 把它大部分隐藏起来的原因:那里有太多东西需要理解。

但这也是 为什么Git 通过将提交与它们的父母进行比较来向我们展示提交。显示8858448bb4 的全部内容太多了,所以git show 8858448bb4 向我们展示了8858448bb4 与其父级相比更改 的内容。 Git 对索引采取了相同的策略,向我们展示了我们已更改的内容,而不是倾倒整个内容。

我认为,这就是让人们认为 Git 正在存储更改的原因。 Git 显示 更改,因此 Git 必须 存储 它们……但事实并非如此! Git 存储整个快照。每次您要求 Git 向您展示某些内容时,Git 都会找出变化。

考虑到这一点,让我们看看我们如何查看索引。

索引位于当前提交和工作树之间

我们现在知道每个提交都是一个完整的快照。如果每次提交时 Git 都为每个文件创建一个新副本,那么存储库会很快变得非常大。所以它不这样做,并且它不这样做的方式的一部分非常简单。虽然每个提交一个完整的快照,但每个提交内部的文件是完全、完全、100% 只读的。他们都不能永远改变。这意味着每个提交都可以共享它的部分或全部文件与一些较早的提交!

Git 只需要确保每次我们运行 git commit 时,它冻结所有文件内容,永远 - 或者如果不是永远,至少只要这个新提交继续存在。所以每次提交中的文件都被冻结了。它们也被压缩成一种特殊的 Git-only 格式(这对文本文件非常有效,但对于像图像这样的二进制文件通常不是很好)。这种压缩需要时间,有时甚至是很多时间,但它会使存储库保持较小。

显然,冻结的仅 Git 文件仅对 Git 本身有用,因此我们需要将 当前 提交中的每个文件的副本取出、解冻、解压缩并使其有用。这些有用的副本进入工作树,我们在那里工作。

其他版本控制系统也做同样的事情。在假设的 XYZ 版本控制系统中,您运行 xyz checkout <em>commit</em> 并将提交从深度冻结仓库中复制出来,解冻,解压缩,并将其存储在您的工作树中。你做了一些工作,最终你运行xyz commit。它现在扫描你的整个工作树,重新压缩每个文件,冻结它,并检查它是否已经在仓库中获得了那个冻结的版本,或者需要把这个也放在那里。当您去喝咖啡或其他什么时,这些步骤中的每一个都需要数秒或数分钟。

Git 的索引非常聪明:索引是一个暂存区,位于深度冻结仓库(充满提交的存储库)和有用的形式(解冻工作树中的文件)。最初,它包含深度冻结中的相同文件。它们已经解冻(在某种程度上),但仍处于特殊的仅 Git 形式,并且它们与您的工作树中完全解冻的解压缩版本配对。

当您更改工作树中的文件或添加和/或删除文件时,索引副本与工作树不同步。现在 Git 可以比较索引副本与工作树副本,并告诉您已更改但尚未暂存的内容

一旦你以你想要的方式获得了一些文件,你就可以运行git add <em>file</em>。这会立即将文件重新压缩,以特殊的仅 Git 格式,并将该副本放入索引中。现在索引副本——它是一个完整的副本,只是压缩了——与 work-tree 副本匹配,但与 committed 副本不同。

您可以随时让 Git 将每个文件的 提交 (HEAD) 副本与 index 副本进行比较:

git diff --cached

对于相同的文件,Git 什么也没说。对于不同的文件,Git 会列出文件并向您显示差异。

同样,您可以随时让 Git 将每个文件的 index 副本与 work-tree 副本进行比较:

git diff

对于相同的文件,Git 什么也没说。对于不同的文件,Git 会列出文件并向您显示差异。

(注意:添加--name-statusgit diff 显示文件的名称,前缀为M 表示修改,如果它们被修改。Git 使用A 表示新添加的文件,@987654351 @ 表示已删除的文件,依此类推。只需将文件从索引中完全删除即可在索引中删除。如果文件在索引中,则在索引中添加索引但不在HEAD。)

git status 命令使用--name-status 限制器运行这两个比较。对于HEAD 和索引之间不同的文件,这些文件暂存以供提交。对于索引和工作树之间不同的文件,它们不会暂存


如图:

   HEAD         index        work-tree
----------    ----------    ----------
README.txt    README.txt    README.txt
main.py       main.py       main.py

HEAD 副本被冻结,因为它在提交中。索引和工作树副本可以更改,但最初,所有三个都匹配。您更改工作树副本并使用 git add 将其复制回 索引,压缩并对其进行 en-Git-ing(如果“en-Git-ing”是一个词,它不是)。如果您根本不想在索引中更改它,您可以使用git reset(使用其默认的--mixed 操作,或者它在任何单个文件上的工作方式)将冻结的文件复制回索引中。

这也是为什么git commitxyz commit 快的原因

当您运行git commit 时,Git 已经拥有所有将在新提交中以正确格式进入的文件。它不必重新压缩所有工作树文件并查看它们是否与冻结的提交版本匹配。 index 已准备就绪:它所要做的就是冻结索引副本,如果这与之前的提交相同,与以前的提交。

此外,由于索引“知道”哪些文件与工作树匹配,哪些文件不匹配,1 并且还有关于存储库中内容的额外信息,这使得 git checkout 更快也。假设您在 master 上拥有大约 3500 个文件,而您在 git checkout 上拥有大约 3300 个文件的其他分支都完全相同。两次提交之间大约有 200 个文件不同(可能还有一些是新的或已删除的)。 Git 可以使用 index 了解它可能需要在 work-tree 中触及的内容,并完全避免触及那些大约 3300 个文件。

因此,与 XYZ 系统扫描并可能接触 3500 个文件不同,Git 扫描并可能接触 200 个文件,节省了超过 94% 的工作。


1这通常需要扫描工作树。索引保留了(caches)数据关于工作树的副本,以加快处理速度。这就是索引有时被称为 cache 的原因。其他 VCS,例如 Mercurial,有一个工作树缓存(Mercurial 将此称为 dirstate),但与 Git 的索引不同,它被正确隐藏:您不必知道它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-18
    • 2014-05-23
    • 1970-01-01
    • 2017-10-27
    相关资源
    最近更新 更多