gpt4 book ai didi

git - “committed”和 “unmodified”是否相同?

转载 作者:行者123 更新时间:2023-12-02 09:07:53 25 4
gpt4 key购买 nike

我从https://git-scm.com/book/en/v2/Getting-Started-What-is-Git%3F读到有关gitt中的三种状态
它在这里说Git具有文件可以驻留在其中的三个主要状态:已提交,已修改和已暂存。

然后,我还阅读了有关的两种状态:https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository中已跟踪或未跟踪的
这里说您的工作目录中的每个文件可以处于两种状态之一:已跟踪或未跟踪。跟踪的文件是最后一个快照中的文件;它们可以是,未经修改的,已修改或上演的。

中提到的状态是否与跟踪文件子状态类似? 提交的未经修改的是否相同?

这些图像显示它们是相同的吗?

The lifecycle of the status of your files

The three file states for Git: modified, staged, and commited

最佳答案

TL; DR

跟踪性不是所列三个状态的子集,并且所列三个状态不足以描述(或真正理解)Git的工作方式。



这“三个状态”有点像个白话,这可能就是页面上说的原因:

Git has three main states



(强调我的)。我认为Pro Git本书在这里有点不利,因为我认为他们出于某些充分的理由正试图从最初的一切观点中隐藏Git索引的存在。但是在同一段中,他们介绍了暂存区域的概念,实际上这只是索引的另一个名称。

实际上,这里真正发生的是每个文件通常有三个副本。当前提交中有一个副本,索引/暂存区中有一个副本,工作树中有第三个副本。

从版本控制系统设计的角度来看,中间副本(索引中的副本)不是必需的。 Mercurial是另一个非常类似于Git的版本控制系统,每个文件只有两个副本:已提交的一个和工作树的一个。这个系统更容易思考和解释。但是出于种种原因,1 Linus Torvalds决定您应该在提交和工作树之间插入第三份副本。

知道已提交的文件副本采用特殊的冻结,只读,压缩,仅Git的文件格式(这是Git称为blob对象,尽管您通常不需要知道)的格式很有用。因为这样的文件是冻结的/只读的,所以Git可以在使用同一文件副本的每次提交中共享它们。这样可以节省大量磁盘空间:一个10兆字节文件的一次提交最多占用10兆字节(取决于压缩),但是使用同一文件进行第二次提交,而新副本则占用零个额外字节:它只是重复使用现有副本。不管您进行了多少次提交,只要您继续重复使用旧文件,就不会占用更多空间来存储文件。 Git只是继续重复使用原始版本。

实际上,关于提交的所有内容都会永久冻结。任何提交的任何部分-文件,作者信息,日志消息中的拼写错误-都无法更改。您能做的最好的事情就是进行新的改进的,不同的提交,以解决拼写错误或其他问题。然后,您可以使用新的和改进的提交,而不是旧的和糟糕的提交,但是新的提交是具有不同哈希ID的其他提交。哈希ID是提交(和就此而言,提交快照附带的Blob对象)的真实名称。

因此,提交是永久的并且是只读的。提交中的文件被压缩为只读,仅Git的冻干格式。由于提交是历史记录,因此可以永久保留历史记录,以防万一您想回头看看某人的行为,时间和原因。但这根本无法完成任何实际工作。您需要手中的文件具有可延展性,柔韧性,可塑性,易处理性,柔性,腻子。您需要使用文件。简而言之,您需要一个工作树,您可以在其中完成实际的工作。

当您对提交进行 git checkout编码时,Git将冻干的副本提取到该工作树中。现在您的文件都在那里,您可以在其中使用和更改它们。您可能会认为 git commit将从工作树中获取更新的文件并提交它们,例如Mercurial的 hg commit所做的那样,但是不,这不是Git所做的。

相反,Git在提交的副本和工作树副本之间插入每个文件的此第三副本。这个第三副本位于Git有时称为索引,有时称为暂存区域以及偶尔调用缓存的实体中(一件事情的三个名称)采用的是冻干Git格式,但重要的是,因为它不在一次提交,您可以随时覆盖它。这就是 git add的作用:它将您在工作树中拥有的一个普通文件,冷冻干燥,然后将其填充到索引中,以代替之前该名称下的索引。

如果文件不在 git add之前的索引中,那么现在就可以了。而且,如果它在索引中……那么,无论哪种情况,Git都将工作树文件压缩为适当的冻干格式,然后将其填充到索引中,因此现在索引副本与工作树副本匹配。如果工作树副本与提交的副本匹配(适当地采用任何冻干或补水的方式),则所有三个副本都匹配。如果没有,您可能有两个匹配的副本。 但这不是的唯一可能性-它们只是主要的三种,稍后我们将看到。

1这些原因中的大多数归结为性能。 Git的 git commit比Mercurial的 hg commit快数千倍。其中一些是因为Mercurial主要是用Python编写的,但很大一部分是因为Git的索引。

2更确切地说,提交将一直持续到没有人再通过哈希ID找到它们为止。当您从旧的,糟糕的提交切换到新的和改进的副本时,可能会发生这种情况。之后,如果旧的和糟糕的提交确实无法找到(而不是仅仅从偶然的观察中隐藏),则可以被Git的垃圾收集器 git gc删除。

对于每个文件,请检查其三个副本中的状态

您已经通过 HEAD选择了一些提交作为当前( git checkout)提交。 Git发现此提交有一定数量的文件。它已将它们全部提取到索引和工作树中。假设您只有 README.mdmain.py文件。他们现在是这样的:
  HEAD           index        work-tree
--------- --------- ---------
README.md README.md README.md
main.py main.py main.py

从此表中很难分辨出哪个文件具有哪个版本,因此让我们添加一个版本号:
  HEAD           index        work-tree
--------- --------- ---------
README.md(1) README.md(1) README.md(1)
main.py(1) main.py(1) main.py(1)

这与Pro Git书的第一个状态相匹配。

现在,您可以修改工作树中的文件之一。 (这些是您可以使用普通的非Git命令看到并使用的唯一文件。)假设您将 README.md的版本2放入工作树:
  HEAD           index        work-tree
--------- --------- ---------
README.md(1) README.md(1) README.md(2)
main.py(1) main.py(1) main.py(1)

Git现在会说您尚未针对 README.md提交更改。这的真正含义是,如果我们进行两次比较(从 HEAD与index开始,然后转到索引与工作树),则在第一次比较中我们看到的是相同的,而在第二次比较中则有所不同。这与Pro Git书的“已修改但未上演”状态相符。

如果现在运行 git add README.md,Git将冻结干燥更新的工作树版本2 README.md并覆盖索引中的一个:
  HEAD           index        work-tree
--------- --------- ---------
README.md(1) README.md(2) README.md(2)
main.py(1) main.py(1) main.py(1)

该表中的一个细微变化是,现在,在比较中, HEAD -vs-index显示 README.md已更改,而index-vs-work-tree显示它们相同。 Git称这种情况为提交阶段进行了更改。这与Pro Git书的“修改和上演”状态相匹配。

如果您现在进行一次新提交,则Git将立即打包索引中的所有内容(即版本1 main.py和版本2 README.md),并使用这些文件进行新提交。然后它将进行调整,以便 HEAD表示新提交,而不是您先前 checkout 的提交。因此,现在,即使旧的提交仍以版本1形式保留两个文件,您现在仍具有:
  HEAD           index        work-tree
--------- --------- ---------
README.md(2) README.md(2) README.md(2)
main.py(1) main.py(1) main.py(1)

现在 README.md的所有三个副本都匹配了。

但是,假设您现在在工作树中更改 README.md来制作版本3,然后 git add变成:
  HEAD           index        work-tree
--------- --------- ---------
README.md(1) README.md(3) README.md(3)
main.py(1) main.py(1) main.py(1)

然后,您需要进一步更改 README.md来创建版本4,该版本不同于之前的所有三个版本:
  HEAD           index        work-tree
--------- --------- ---------
README.md(1) README.md(3) README.md(4)
main.py(1) main.py(1) main.py(1)

现在,当我们比较 HEAD -vs-index时,我们看到 README.md已为提交而暂存,但是当我们比较索引与工作树时,我们也未将其暂存为commit。这与这三种状态都不匹配,但是有可能!

跟踪与未跟踪

Tracked files are files that were in the last snapshot ...



不幸的是,这是高度误导的。实际上,一个被跟踪的文件就是现在索引中的任何文件。注意该指数是可延展的。它现在可能具有 README.md版本3,但是您可以将 README.md替换为另一个版本,甚至完全删除该 README.md

如果删除该 README.md,则会得到:
  HEAD           index        work-tree
--------- --------- ---------
README.md(1) README.md(4)
main.py(1) main.py(1) main.py(1)

现在版本3才消失。3因此,工作树中的 README.md现在是未跟踪的文件。如果在运行 README.md之前将 git commit的一个版本(任何版本)放回索引中,则 README.md将返回到被跟踪的位置,因为它在索引中。

由于 git checkout会从您 check out 的提交中填充索引(和工作树),因此可以说可以跟踪上次提交中的文件是正确的。但是,正如我在这里所说的那样,这具有误导性。跟踪度是文件在索引中的函数。它如何到达那里与跟踪性无关。

3从技术上讲,Git在其对象数据库中仍将冷冻干燥的副本作为blob对象,但是如果没有其他人使用该冷冻干燥的副本,则它现在可以进行垃圾收集,并且随时可能消失。

Git从索引中进行新的提交;新的提交参考旧的提交

上面我们已经提到了其中的一些内容,但是让我们再看一遍,因为这对于理解Git至关重要。

Git中的每个提交(实际上是任何类型的每个对象)都有特定于该特定提交的哈希ID。如果您记下哈希ID,然后再次键入所有哈希ID,只要该提交仍位于Git的“所有对象”的主数据库中,则Git可以使用该哈希ID查找提交。

每个提交中还存储了一些早期提交的哈希ID。通常,这只是一个先前的哈希ID。此先前的哈希ID是提交的父级。

每当您(或Git)拥有这些哈希ID之一时,我们就说您(或Git)具有指向基础对象的指针。因此,每个提交都指向其父级。这意味着,给定一个只有三个提交的小型存储库,我们可以绘制这些提交。如果我们使用单个大写字母来表示提交哈希ID,则结果对人类更有用,尽管我们当然会很快用完ID(因此,我们绘制的不仅仅是几次提交):
A <-B <-C

这里 C是最后的提交。我们必须以某种方式知道其哈希ID。如果这样做,我们可以让Git从数据库中获取实际的提交,并且 C保留其前身提交 B的哈希ID。我们可以让Git使用它来获取 B并找到 A的哈希ID。我们可以使用它来获取 A本身,但是这次没有以前的哈希ID。不可能是: A是第一个提交。 A没有指向之前的提交。

所有这些指针总是根据需要指向后方。提交后,任何提交的任何部分都无法更改,因此 B可以保留 A的ID,但是我们无法更改 A来将 B的ID填充为 AC可以指向 B,但是我们不能更改 B使其指向 C。但是我们要做的只是记住 C的真实哈希ID,而 就是中分支名称的所在。

让我们选择名称 master并让Git在该名称下保存 C的哈希ID。由于名称包含哈希ID,因此名称指向 C:
A--B--C   <-- master

(由于懒惰和/或其他原因,我已停止在提交中以箭头的形式绘制连接器。没关系,因为它们无法更改,我们知道它们指向后。)

现在,我们使用 C来检查commit git checkout master,它从使用commit C保存的文件中填充索引和工作树:
git checkout master

然后,我们将修改一些文件,使用 git add将它们复制回索引中,最后运行 git commitgit commit命令将收集我们的姓名和电子邮件地址,从我们或 -m标志获取日志消息,添加当前时间,并通过保存当前索引中的内容进行新提交。这就是为什么我们必须首先将文件 git add到索引。

此新提交将具有 C的哈希ID作为新提交的父级。写出提交的动作将计算新提交的哈希ID,但我们将其称为 D。所以我们现在有:
A--B--C   <-- master
\
D

但是现在Git做了一些非常聪明的事情:它将 D的哈希ID写入名称 master,因此 master现在指向 D:
A--B--C
\
D <-- master

现在提交 D是最后一次提交。我们只需要记住名字 master; Git会为我们记住哈希ID。

git commit -a呢?

Git确实可以使用 git commit -a来提交工作树中的所有内容。但是实际上,这实际上是在执行提交之前立即运行 git add -u:对于当前实际上在索引中的每个文件,Git都会检查工作树副本是否不同,如果存在,Git会添加该文件到索引。然后,它根据索引进行新的提交。4

每个文件的中间第三个副本(索引中的一个副本)是为什么您始终需要 git add的原因。作为Git的新用户,它通常会妨碍您。尝试使用 git commit -a解决它,并假装它不存在。但这最终会导致您在索引出现问题而失败时陷入困境,并且使被跟踪的文件与未跟踪的文件完全无法解释。

而且,索引的存在允许各种巧妙的技巧,例如 git add -p,对于某些工作流程实际上非常有用且实用,因此了解索引并不是一个坏主意。您可以将其保留很多以供以后使用,但请记住,有一个中间的冻干副本,并且 git status运行两个比较- HEAD -vs-index,然后是index-vs-work-tree,这一切都做得更多。感。

4这也是一个骗人的谎言:Git实际上为这种情况做了一个临时索引。临时索引从真实索引的副本开始,然后Git在此处添加文件。但是,如果提交工作一切顺利,则临时索引将成为索引(即原来的真实主索引),因此添加到临时索引具有相同的效果。出现的唯一时间是提交失败时,或者如果您足够偷偷摸摸,则是在 git commit -a仍在进行中时检查存储库状态。

如果使用 git commit --only,图片会变得更加复杂,它会生成两个临时索引(索引?)。但是,让我们不要去那里。 :-)

关于git - “committed”和 “unmodified”是否相同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55877484/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com