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


486

有人可以告诉我git中HEAD,工作树和索引之间的区别吗?

据我了解,它们都是不同分支的名称。我的假设正确吗?


编辑

我找到了这个

单个git存储库可以跟踪任意数量的分支,但是您的工作树仅与其中一个分支(“当前”或“签出”分支)相关联,并且HEAD指向该分支。

这是否意味着HEAD和工作树始终相同?


26
关于您的编辑:绝对不是。HEAD是当前分支顶端的提交。如果您刚刚签出了分支,即没有修改过的文件,则其内容与工作树匹配。一旦您修改了任何内容,它便不再匹配。
卡斯卡贝尔

6
我认为您必须阅读以下内容:think-like-a-git.net
AndrzejDuś2014年

5
我还将Staging Area在该列表中添加一个。什么是HEADWorking TreeIndexStaging Area
格林

2
@Jefromi的最后一句话将更清楚地显示为:>一旦您进行了任何修改,工作树将不再与HEAD提交匹配
starscream_disco_party

3
对于将来的任何阅读,真正了解其中一些答案的最佳方法是观察和感受并可视化正在发生的事情:这是学习git的最佳工具:onlywei.github.io/explain-git-with -d3 /#fetchrebase
BKSpurgeon'7

Answers:


577

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

替代文字

我使用索引作为检查点

当我要进行可能会出错的更改时–当我想探索某个方向时,我不确定是否可以继续进行下去,甚至不确定这是否是一个好主意,例如从概念上讲要求重构或更改表示形式-将工作点检查到索引中。如果这是自上次提交以来我进行的第一次更改,则可以将本地存储库用作检查点,但是通常我会通过一系列小步骤来实现一个概念上的更改。我想在每个步骤之后都检查点,但是保存提交,直到我回到工作正常,经过测试的代码为止。

笔记:

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

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

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

不要认为上图中的四个“磁盘”是回购文件的单独副本。

替代文字

它们基本上是Git提交的命名参考。引用主要有两种:标记和标头。

  • 标签是标记历史中特定点的固定引用,例如v2.6.29。
  • 相反,负责人总是动摇以反映项目开发的当前位置。

替代文字

(注意:正如Timo Huovinen评论的 那样,那些箭头不是提交所指向的,而是工作流顺序,基本上将箭头显示为第一次提交和最后一次提交在哪里)1 -> 2 -> 3 -> 414

现在我们知道了项目中正在发生的事情。
但是要知道这里发生了什么,现在有一个称为HEAD的特殊参考。它有两个主要目的:

  • 它告诉Git在结帐时从哪个提交获取文件,以及
  • 它告诉Git在提交时在哪里放置新提交。

运行时,git checkout ref它指向HEAD您指定的引用并从中提取文件。运行时,git commit它将创建一个新的提交对象,该对象将成为current的子对象HEAD。通常HEAD指向一个脑袋,所以一切正常。

替代文字


20
在阅读了很多有关git的内容之后,我从未完全理解它,我真的很沮丧n我想使用f字;但是我在社区中!您已经提到了磁头,但是在上图中,始终有一个HEAD,其余的f个磁头呢?“通常HEAD指向其中一个头,因此一切正常。” 乌尔声明,我谨对此解释。
Necktwi 2014年

12
@neckTwi HEAD是您正在使用的当前提交stackoverflow.com/a/964927/6309)。它通常是“分支头”之一(由分支引用的提交之一,代表所述分支的尖端)。但是您可以签出(并继续进行)任何提交。如果签出不是分支分支之一的提交,则您处于“分离头”模式:stackoverflow.com/a/3965714/6309
VonC 2014年

1
@Imray我同意,但是这是我发现的这些图片5年前(hades.name/blog/2010/01/28/...
VonC

11
关于索引,我认为可以说最有用的是“索引只是暂存区的另一个名称”,就像@ ashraf-alam一样。我觉得大多数时候都在讨论中将其称为登台区域,这就是为什么我没有自动建立与索引相同的连接的原因。
皮特2016年

1
@Pete我同意。有关缓存和索引之间的区别的更多信息,请参见我的其他答案stackoverflow.com/a/6718135/6309
VonC 2016年

136

“ 1.3 Git基础的“三种状态”部分描述了HEAD(当前分支或当前分支上的最后提交状态),索引(又称为登台区域)和工作树(签出文件的状态)之间的差异。章临Git的书由Scott Chacon的(创作共用许可)。

这是从本章说明的图像:

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

在上图中,“工作目录”与“工作树”相同,“暂存区”是git“索引”的备用名称,HEAD指向当前已签出的分支,提示指向“ git目录(存储库)”

请注意,这git commit -a将分阶段进行更改并一步提交。


1
“一张图片胜过千言万语”。感谢Jakub ..也感谢您的链接。
乔伊斯·巴布

5
注意:如今working tree似乎是首选working directory。见github.com/git/git/commit/...
VonC

2
由于暂存区包含在一个称为“索引”的文件中,并且该索引文件恰好位于.git目录的根目录中,因此该图片并不完全准确。因此,如果将存储库定义为.git目录,则暂存区在技术上就位于存储库内。第三列最好标记为“ HEAD的根树对象”,以指示检出的文件来自提交对象,并且提交将新树写入到提交对象-这两个提交对象均由HEAD指向。
Jazimov

@Jazimov您可能是对的,但正如他所写的那样,他是从著名的Pro Git书中拍摄的照片,并且提供了链接。因此,如果情况可以改善或什至是错误的,那么应该有人告诉那本书的作者...总的来说,我愿意这样做,但是说实话,我仍然是git初学者,还没有明白你说的话,所以在那种情况下我绝对是错误的人。
比纳罗斯(Binarus)'17

@Binarus:像这样的大量复制图像的危险在于,它会传播一个作者/书本所做出的“虚假陈述”。我认为这是文字解释与功能解释的一种情况:从字面意义上讲,如果将仓库定义为.git文件夹下的所有内容,则实际上索引包含在仓库中。但是,从功能上来说,该索引有助于Git在回购协议中维护DAG,并且可以认为它是外部的。
Jazimov '17

64

您的工作树就是当前正在处理的文件中的实际内容。

HEAD是指向您上次签出的分支或提交的指针,并且如果您进行了创建,它将成为新提交的父级。例如,如果您在master分支上,HEAD则将指向master,并且在您提交时,该新提交将是所master指向的修订版本的后代,master并将被更新以指向新提交。

指数是一个临时区域,其中新提交准备。本质上,索引的内容将是新提交的内容(尽管如果这样做git commit -a,它将在提交前自动将Git知道的文件的所有更改添加到索引中,因此它将提交工作树的当前内容)。git add会将工作树中的文件添加或更新到索引中。


非常感谢Brian的解释。因此,工作树包含所有未提交的更改。如果我使用git commit -a提交更改,那么在那个特定时间我的工作树和索引将是相同的。当我推送到我的中央存储库时,所有三个都将相同。我对么?
Joyce Babu 2010年

3
@Vinod差不多。您可以在工作树中拥有Git不知道的文件,而这些文件不会被提交git commit -a(您需要使用来添加git add文件),因此您的工作树中可能会有索引,本地存储库或其他文件。您的远程仓库没有。
Brian Campbell 2010年

2
@Vinod:工作树和索引可以变得相同而无需提交(git add从工作树更新索引,而git checkout <path>从索引更新工作树)。HEAD指的是最近的提交,因此在提交时,您将更新HEAD为与索引匹配的新提交。推送与它没有多大关系,它使本地仓库中的远程匹配分支成为分支。
卡斯卡贝尔

45

工作树

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

Git指数

  • git“索引”是放置要提交到git存储库的文件的地方。

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

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

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

  • 索引不是git存储库:git索引中的文件是git如果提交到git commit命令将提交到git存储库的文件。


1
请注意,Git 2.5将带来多个工作树(stackoverflow.com/a/30185564/6309)。+1
VonC

3
我不确定“索引不是工作目录”是否正确100%。它应该是“索引不是工作目录,但是包括整个工作目录以及您接下来要提交的更改”。证明?转到git存储库,reset --hard HEAD以确保索引==您的工作树。然后:mkdir history && git checkout-index --prefix history/ -a结果是您history/目录中整个工作树的重复。Ergo git index> = git工作目录
Adam Kurkiewicz

3
索引不是工作目录,并且不必包括工作目录。索引只是git存储库中的一个文件,用于存储您要提交的信息。
Boon 2015年

3
““索引”保存着工作树内容的快照,而该快照将作为下一次提交的内容。因此,在对工作目录进行任何更改之后,在运行commit命令之前,您可以必须使用add命令任何新的或修改过的文件添加到索引”(git-scm.com/docs/git-add

3
@AdamKurkiewicz:如果您先echo untracked-data > untracked-filegit reset --HARDgit checkout-index步骤之后或之后进行验证,则证明将失败。你会发现,在未跟踪文件是不是history目录中。您也可以单独修改索引和工作树,尽管在不先触摸工作树的情况下修改索引就很困难(需要使用git update-index --index-info)。
torek '17
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.