1

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

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

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

4

1 回答 1

2

TL;博士

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

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

在 Git 中,提交包含所有文件的完整快照。它还包含一些元数据——关于提交本身的信息,例如您的姓名、电子邮件地址和时间戳。元数据中包含提交的提交的哈希 ID——或者,对于合并提交,多个父提交,复数——通过提交与其父提交进行比较,Git 会向您显示更改。每个提交都有自己唯一的哈希 ID,例如8858448bb49332d353febc078ce4a3abcc962efe(这是 Git 存储库中 Git 的提交的 ID)。该提交是一个快照,但该提交有一个父级(在本例中为67f673aa4a...,因此 Git 可以通过提取较早的两个8858448bb4...67f673aa4a 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向我们展示了,与其父级的变化。8858448bb4Git 对索引采取相同的策略,向我们展示我们所做的更改,而不是倾倒整个事情。

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

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

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

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

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

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

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

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

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

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

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

git diff --cached

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

同样,您可以随时让 Git 将每个文件的索引副本与工作树副本进行比较:

git diff

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

(注意:添加--name-statusgit diff显示文件的名称,前缀M为修改,如果它们被修改。GitA用于新添加的文件,D用于删除的文件,等等。在索引中删除的文件是只需将其从索引中完全删除。如果文件在索引中但不在索引中,则将其添加HEAD到索引中。)

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


图示:

   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 commit这么快的原因,相比xyz commit

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

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

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


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

于 2018-12-02T19:26:00.443 回答