在Git中,HEAD,工作树和索引之间有什么区别?

Joy*_*abu 453 git version-control

有人能告诉我在Git中HEAD,工作树和索引之间的区别吗?

据我所知,它们都是不同分支的名称.我的假设是否正确?


编辑

我找到了这个

单个git存储库可以跟踪任意数量的分支,但是您的工作树只与其中一个分支相关联("当前"或"已检出"分支),HEAD指向该分支.

这是否意味着HEAD和工作树总是一样的?

Von*_*onC 550

关于这些主题的一些其他好的参考:

替代文字

我使用索引作为检查点.

当我即将做出可能出错的改变时 - 当我想探索某个方向时,我不确定我是否可以继续,或者是否这是一个好主意,例如概念要求重构或更改表示类型 - 我检查我的工作到索引.如果这是我自上次提交以来所做的第一次更改,那么我可以使用本地存储库作为检查点,但通常我有一个概念上的更改,我正在实现一组小步骤.我希望在每个步骤之后检查点,但保存提交,直到我回到工作,测试代码.

笔记:

  1. 工作区是你看到的(源)文件和编辑目录树.

  2. 指数是一个单一的,大型的二进制文件<baseOfRepo>/.git/index,其中列出了当前分支中的所有文件,它们的SHA1校验和,时间戳和文件名-它不是在它的文件副本的其他目录.

  3. 本地资源库是一个隐藏目录(.git),包括objects含回购每个文件的所有版本(本地分支机构和远程分支机构的副本),为压缩"斑点"文件目录.

不要将上图中表示的四个"磁盘"视为repo文件的单独副本.

替代文字

它们基本上是Git提交的命名参考.有两种主要类型的参考:标签和头部.

  • 标签是标记历史中特定点的固定引用,例如v2.6.29.
  • 相反,总是移动头来反映项目开发的当前位置.

替代文字

(注意:正如Timo Huovinen 评论的 那样,这些箭头不是提交所指向的,它是工作流程顺序,基本上显示箭头,因为第一次提交的位置是最后一次)1 -> 2 -> 3 -> 414

现在我们知道项目中发生了什么.
但要知道这里发生了什么,现在有一个特殊的参考叫做HEAD.它有两个主要目的:

  • 它告诉Git哪个提交从结账时拿走文件,然后
  • 它会告诉Git在提交时将新提交放在哪里.

当你运行git checkout ref它时指向HEAD你指定的引用并从中提取文件.当您运行git commit它时,它会创建一个新的提交对象,该对象将成为当前的子对象HEAD.通常HEAD指向其中一个头部,所以一切都很好.

替代文字

  • 经过多次阅读git很多次,我从来没有完全理解它,我真的很沮丧,我想用f字; 但我在社区!你已经提到了头部,但是在上面的图片中总是有一个HEAD,剩下的头部是什么?"通常HEAD指向其中一个头部,所以一切正常." 我请你解释一下,乌尔的声明. (18认同)
  • @neckTwi HEAD是您正在使用的**当前提交**(http://stackoverflow.com/a/964927/6309).它通常是"分支头"之一(分支引用的提交之一,代表所述分支的*tip*).但是您可以签出(并处理)任何提交.如果您签出的提交不是(分支)头之一,则您处于"分离的HEAD"模式:http://stackoverflow.com/a/3965714/6309 (10认同)
  • 关于该指数,我认为可以说最有用的是"该指数只是临时区域的另一个名称",如@ ashraf-alam所说.我觉得*大多数时候*在讨论中它被称为临时区域,这就是为什么我没有自动建立与索引相同的连接. (9认同)

Jak*_*ski 124

HEAD(当前分支上的当前分支或最后提交状态),索引(也称为暂存区域)和工作树(结帐中的文件状态)之间的区别在"1.3 Git Basics " 的"三态"部分中描述Scott Chacon 的Pro Git一书(Creative Commons行货).

以下是本章的图片说明:

本地操作 - 工作目录与登台区域(索引)与git存储库(HEAD)

在上面的图像中,"工作目录"与"工作树"相同,"暂存区域"是git"index"的备用名称,HEAD指向当前签出的分支,该尖端指向最后一次提交" git目录(存储库)"

请注意,这git commit -a将在一个步骤中进行更改和提交.

  • 注意:`working tree`似乎比现在的`working directory`更受欢迎.请参阅https://github.com/git/git/commit/89aef71d0eb5b5e06216c2efbba76cffe17679f7 (4认同)
  • “一张图片胜过千言万语”。谢谢 Jakub .. 谢谢你的链接。 (2认同)
  • 这张图不准确,因为暂存区包含在一个名为"index"的文件中 - 该索引文件恰好位于.git目录的根目录中.因此,如果将repo定义为.git目录,则暂存区域在技术上位于repo内.第三列将更好地标记为"HEAD的根树对象",以指示签出的文件来自提交对象,并且提交将新树写入提交对象 - HEAD指向两个提交对象. (2认同)

Bri*_*ell 59

您的工作树就是您当前正在处理的文件中的实际内容.HEAD是指向您上次检出的分支或提交的指针,如果您创建它,它将是新提交的父级.例如,如果您在master分支上,那么HEAD将指向master,并且当您提交时,新提交将是master指向的修订的后代,并且master将更新为指向新提交.

索引是准备新提交的暂存区域.从本质上讲,该指数的内容是什么将进入新的提交(但如果你这样做git commit -a,这将自动添加所有改变了Git知道犯前的索引文件,所以它会提交你的工作树的当前内容).git add将工作树中的文件添加或更新到索引中.

  • @Vinod差不多.您可以在工作树中拥有Git不知道的文件,并且这些文件不会通过`git commit -a`提交(您需要使用`git add`添加它们),因此您的工作树可能有额外的您的索引,本地存储库或远程仓库所没有的文件. (3认同)
  • @Vinod:工作树和索引可以在没有提交的情况下变得相同(git add从工作树更新索引,git checkout <path>从索引更新工作树).`HEAD`指的是最近的提交,所以当你提交时,你正在将`HEAD`更新为与索引匹配的新提交.推送与它没有太大关系 - 它在您的本地仓库中的远程匹配分支中创建分支. (2认同)

Ash*_*lam 40

工作树

您的工作树是您当前正在处理的文件.

Git指数

  • git"index"是您要将文件放置到git存储库的位置.

  • 索引也称为缓存,目录缓存,当前目录缓存,暂存区域,暂存文件.

  • 在"提交"(签入)文件到git存储库之前,需要先将文件放在git"index"中.

  • 索引不是工作目录:您可以键入命令,例如git status,并且git将告诉您工作目录中的哪些文件已添加到git索引(例如,通过使用该git add filename命令).

  • 索引不是git存储库:git索引中的文件是git将使用git commit命令提交给git存储库的文件.

  • 我不确定"索引不是工作目录"是100%正确的.它应该是"索引不是工作目录,但它包括整个工作目录+您希望下次提交的更改".证明?转到git仓库,`reset --hard HEAD`以确保你的索引==你的工作树.然后:`mkdir history && git checkout-index --prefix history/-a`结果是你的`history /`目录中整个工作树的重复.Ergo git index> = git工作目录 (3认同)
  • 索引不是工作目录,不必包含工作目录.索引只是git存储库中的一个文件,用于存储您要提交的信息. (3认同)
  • ""索引"包含工作树内容的快照,正是这个快照被视为下一次提交的内容.因此,在对工作目录进行任何更改之后,在运行commit命令之前,必须使用add命令将任何新的或修改过的文件添加到索引"(https://git-scm.com/docs/git-add) (3认同)
  • @AdamKurkiewicz:如果您在git reset --HARD和git checkout-index步骤之前或之后首先执行“ echo untracked-data&gt; untracked-file”,则证明失败。您会在“ history”目录中发现* untracked *文件不是* not *。您也可以独立地修改索引和工作树,尽管不先接触工作树就很难修改索引(需要使用git update-index --index-info`)。 (3认同)

Meh*_*hdi 19

这是ProGit 书中不可避免的冗长但易于理解的解释:

注意:作为参考,您可以阅读本书的第 7.7 章,重置揭秘

Git 作为一个系统在其正常运行中管理和操作三棵树:

  • HEAD:上次提交快照,下一个父级
  • 索引:提议的下一次提交快照
  • 工作目录:沙盒

头部

HEAD是指针当前分支的参考,这是又一个指针到最后提交该分支上制成。这意味着 HEAD 将是创建的下一个提交父级。将 HEAD 视为您在该分支上最后一次提交快照通常是最简单的。

它包含什么?
要查看该快照的样子,请在存储库的根目录中运行以下命令:

                                 git ls-tree -r HEAD
Run Code Online (Sandbox Code Playgroud)

它会导致这样的事情:

                       $ git ls-tree -r HEAD  
                       100644 blob a906cb2a4a904a152... README  
                       100644 blob 8f94139338f9404f2... Rakefile  
                       040000 tree 99f1a6d12cb4b6f19... lib  
Run Code Online (Sandbox Code Playgroud)

指数

Git 使用上次检出到您的工作目录中的所有文件内容的列表以及它们最初检出时的外观来填充此索引。然后,您将其中一些文件替换为它们的新版本,然后 git commit 将其转换为新提交的树。

它包含什么?
使用 git ls-files -s 看看是什么样子。您应该会看到如下内容:

                 100644 a906cb2a4a904a152e80877d4088654daad0c859 0 README   
                 100644 8f94139338f9404f26296befa88755fc2598c289 0 Rakefile  
                 100644 47c6340d6459e05787f644c2447d2595f5d3a54b 0 lib/simplegit.rb  
Run Code Online (Sandbox Code Playgroud)

工作目录

这是您的文件所在的位置,您可以在将它们提交到暂存区(索引)然后进入历史记录之前尝试更改。

可视化样本

让我们看看这三棵树是如何一起工作的(正如 ProGit 书中提到的那样)?
Git 的典型工作流程是通过操作这三棵树,以连续更好的状态记录项目的快照。看看这张照片:

在此处输入图片说明

要获得良好的可视化理解,请考虑这种情况。假设您进入一个包含单个文件的新目录。调用此文件的 v1。它以蓝色表示。运行git init将创建一个带有指向未出生主分支的 HEAD 引用的 Git 存储库

在此处输入图片说明

此时,只有工作目录树有任何内容。现在我们要提交这个文件,所以我们使用git add工作目录中的内容并将其复制到索引中。

在此处输入图片说明

然后我们运行git commit,它获取索引的内容并将其保存为永久快照,创建一个指向该快照的提交对象,并更新 master 以指向该提交。

在此处输入图片说明

如果我们运行git status,我们将看不到任何变化,因为所有三棵树都是相同的

美丽的点

git status 以下列方式显示这些树之间的差异:

  • 如果工作树与索引不同,git status则将显示有一些更改未暂存以进行提交
  • 如果工作树与索引相同,但与 HEAD 不同,则将在其结果中git status显示更改要提交部分下的一些文件
  • 如果工作树是从指数不同,指数是从头部不同,那么git status会显示一些文件下的变化不会上演提交下段和一些其他文件的更改要提交其结果部分。

对于更多的好奇

关于git reset命令的注意事项
希望了解reset命令的工作原理将进一步阐明这三棵树存在背后的原因。

resetcommand 是您在 git 中的 Time Machine,它可以轻松地带您回到过去并带来一些旧快照供您处理。通过这种方式,HEAD 是您可以穿越的虫洞。让我们用书中的一个例子来看看它是如何工作的:

考虑以下存储库,它具有单个文件和 3 个以不同颜色和不同版本号显示的提交:

在此处输入图片说明

树木的状态如下图所示:

在此处输入图片说明

第 1 步:移动头部(--soft):

reset 要做的第一件事是移动 HEAD 指向的内容。这与更改 HEAD 本身不同(这是 checkout 所做的)。reset 移动 HEAD 指向的分支。这意味着如果 HEAD 设置为 master 分支,运行 git reset 9e5e6a4 将首先使 master 指向 9e5e6a4。如果您reset 使用--softoption调用,它将在此处停止,而不会更改indexworking directory。我们的 repo 现在看起来像这样:
注意:HEAD~ 是 HEAD 的父级

在此处输入图片说明

第二次查看图像,我们可以看到该命令基本上取消了最后一次提交。由于工作树和索引相同但与 HEAD 不同,git status现在将以绿色显示准备提交的更改。

第 2 步:更新索引(--mixed):

这是命令的默认选项

reset使用--mixed选项运行会使用当前 HEAD 指向的任何快照的内容更新索引,而保持工作目录不变。这样做,您的存储库看起来就像您完成了一些未暂存的工作时一样,git status并将其显示为未暂存以提交红色的更改。此选项还将撤消最后一次提交并取消暂存所有更改。就像您进行了更改但尚未调用git add命令。我们的回购现在看起来像这样:

在此处输入图片说明

第 3 步:更新工作目录 (--hard)

如果您reset使用--hard选项调用,它会将 HEAD 指向的快照内容复制到 HEAD、索引和工作目录中。执行 reset --hard 命令后,这意味着你回到了前一个时间点,之后根本没有做任何事情。见下图:

在此处输入图片说明

结论

我希望现在您对这些树有了更好的理解,并且对它们通过使您能够更改存储库中的文件以撤消或重做您错误地做的事情而给您带来的力量有一个很好的了解。