ChatGPT解决这个技术问题 Extra ChatGPT

Git 中的 HEAD 是什么?

git

你会看到 Git 文档说类似

分支必须完全合并到 HEAD 中。

但是 Git HEAD 到底是什么?


G
Greg Hewgill

您可以将 HEAD 视为“当前分支”。当您使用 git checkout 切换分支时,HEAD 版本更改为指向新分支的尖端。

您可以通过执行以下操作查看 HEAD 指向的内容:

cat .git/HEAD

就我而言,输出是:

$ cat .git/HEAD
ref: refs/heads/master

HEAD 可以引用与分支名称无关的特定修订。这种情况称为 detached HEAD


所以 git HEAD 的上下文取决于你所在的 BRANCH,对吗?更进一步,您作为开发人员?我想我在问,Git HEAD 会成为一个存储库范围的全球事物,还是每个开发人员的个人?
@bobobobo:没错, HEAD 就像一个指向当前分支的指针。当您签出不同的分支时,HEAD 会更改为指向新分支。当前的 HEAD 对于每个存储库都是本地的,因此对于每个开发人员来说都是独立的。
@动静能量:HEAD 可以指向任何提交,它不需要是任何分支中的最后一个提交。 (当 HEAD 指向不是分支中最后一次提交的提交时,这就是“分离的 HEAD”)。
HEAD 不是“当前分支”。 Is 是初始化工作树当前状态的提交的名称。在更实际的情况下,它可以被认为是对签出提交的符号引用。
投了反对票。我同意@BenCollins。在我看来,第一句话很令人困惑,可以改写为:“您可以将 HEAD 视为当前分支上的当前提交”
n
nCardot

引用 other people

头只是对提交对象的引用。每个头都有一个名称(分支名称或标签名称等)。默认情况下,每个存储库中都有一个名为 master 的头。一个存储库可以包含任意数量的头。在任何给定时间,都会选择一个头作为“当前头”。这个头是 HEAD 的别名,总是大写”。注意这个区别:“头”(小写)指代存储库中的任何一个命名头;“头”(大写)专门指当前活动的头。这Git 文档中经常使用区分。

here 可以找到另一个快速涵盖 git 内部工作原理(从而更好地理解 head/HEAD)的好资源。引用 (ref:) 或头部或分支可以被视为粘贴在提交历史中的提交上的便利贴。通常它们指向一系列提交的尖端,但可以使用 git checkoutgit reset 等来移动它们。


由此:“每个头都有一个名字”。并且“选择一个头作为”当前头。此头别名为 HEAD”。所以我由此得出结论,“HEAD”并不是指“分离 HEAD”状态的情况。
@gxyd 如果 HEAD 不指向“头”,则它是一个分离的 HEAD。它指向您指定的提交的提交 id(例如,使用 git checkout HEAD~2),它不是已知头的提交 id。有关更详尽的说明,请参阅 eagain.net/articles/git-for-computer-scientists 中的文章。
@Silfheed:一般来说,我认为这个答案在概念上比公认的答案更合理(即使使用小写的“head”来指代一个分支会让很多人感到困惑)。然而,git revert 并不是将分支移至不在尖端的一个很好的例子,因为 git revert 只是创建了一些新的提交,并且仍然将当前分支留在(新的)尖端。
“这不是已知头的提交 ID” - 实际上,分离的 HEAD 可以 指向一个提交 ID,该提交 ID 也是已知头(或多个头)的提交 ID。使它分离的原因是 HEAD 直接指向提交 ID,而不是头。这将影响未来 commitreset 等的行为。
@LarsH:分离的 HEAD 指向提交 ID 而不是引用的好点。您实际上可以通过查看 .git/HEAD 来验证这一点。如果它是分离的,它将包含提交的哈希,即使它与已知的头部相同。如果它被附加,它将包含头部的路径(即'ref:refs/heads/bob')。至于 revert 命令,8 年后我从未发现那个错字。 Git reset 允许您调整特定的头部以指向特定的提交。
C
Community

在这些答案中,有一个可能是微妙但重要的误解。我想我会添加我的答案来清除它。

什么是头?

头是你

HEAD 是一个符号引用,指向您在提交历史中的任何位置。无论你走到哪里,无论你做什么,它都会像影子一样跟随你。如果您提交,HEAD 将移动。如果您结帐,HEAD 将移动。无论您做什么,如果您在提交历史中移动了一个新的位置,HEAD 就会与您一起移动。解决一个常见的误解:您不能脱离 HEAD。这不是分离的 HEAD 状态。如果你发现自己在想:“哦,不,我处于分离的 HEAD 状态!我失去了我的 HEAD!”记住,这是你的 HEAD。头是你。你没有脱离 HEAD,你和你的 HEAD 已经脱离了别的东西。

HEAD 可以附加到什么位置?

HEAD 可以指向一个提交,是的,但通常它不会。让我再说一遍。 通常 HEAD 不指向提交。 它指向分支引用。它附加到那个分支,并且当您执行某些操作(例如,commitreset)时,附加的分支将与 HEAD 一起移动。您可以通过查看引擎盖来了解它所指向的内容。

cat .git/HEAD

通常你会得到这样的东西:

ref: refs/heads/master

有时你会得到这样的东西:

a3c485d9688e3c6bc14b06ca1529f0e78edd3f86

这就是当 HEAD 直接指向提交时发生的情况。这称为分离 HEAD,因为 HEAD 指向的不是分支引用。如果您在此状态下提交,则不再附加到 HEADmaster 将不再与您一起移动。该提交在哪里并不重要。 您可以与您的主分支在同一个提交上,但如果 HEAD 指向提交而不是分支,则它会被分离,并且新提交不会与分支引用相关联。

如果您尝试以下练习,您可以以图形方式查看此内容。从 git 存储库中运行它。你会得到一些稍微不同的东西,但它们的关键位会在那里。当需要直接检查提交时,只需使用从第一个输出中获得的任何缩写哈希(这里是 a3c485d)。

git checkout master
git log --pretty=format:"%h:  %d" -1
# a3c485d:   (HEAD -> master)

git checkout a3c485d -q # (-q is for dramatic effect)
git log --pretty=format:"%h:  %d" -1   
# a3c485d:   (HEAD, master)

好的,所以这里的输出有一个小的差异。直接检查提交(而不是分支)会给我们一个逗号而不是箭头。你怎么看,我们是否处于超然的 HEAD 状态? HEAD 仍然指的是与分支名称关联的特定修订。我们还在 master 分支上,不是吗?

现在尝试:

git status
# HEAD detached at a3c485d

没有。我们处于“分离 HEAD”状态。

您可以使用 git log -1 查看 (HEAD -> branch)(HEAD, branch) 的相同表示。

综上所述

HEAD 是你。它指向您签出的任何内容,无论您身在何处。通常这不是提交,而是一个分支。如果 HEAD 确实 指向一个提交(或标签),即使它是一个分支也指向的同一个提交(或标签),您(和 HEAD)已与该提交(或标签)分离分支。由于您没有附加分支,因此在您进行新提交时,该分支不会跟随您。但是,HEAD 会。


我喜欢这个答案,因为虽然文档描述了真相,但软件定义了真相。 .git/HEAD 是软件认为是 HEAD。
仅就其概念定义而言,这应该是公认的答案。
我真的认为这是最好的答案。其他的暗示HEAD是一个分支的尖端,但它不是,它就像一个磁带头。一个非常简单的查看此操作的方法是 git checkout HEAD~1 返回一个提交,然后 git checkout HEAD~1 返回另一个提交。如果 HEAD 总是引用分支上的最新提交,则第二个命令将与第一个命令相同 - 它不是。第二个命令将您的 HEAD 再向后移动一次,即您现在从分支的尖端进行了两次提交。这很微妙但很重要。
我喜欢你的表达“HEAD is YOU”。
D
DavidRR

HEAD 只是一个特殊的指针,指向您当前所在的本地分支。

来自 Pro Git 书第 3.1 Git Branching - Branches in a Nutshell 章的创建新分支部分:

如果你创建一个新的分支会发生什么?好吧,这样做会为您创建一个新的指针来移动。假设您创建了一个名为 testing 的新分支。您可以使用 git branch 命令执行此操作: $ git branch testing 这会在您当前所在的同一提交处创建一个新指针 Git 如何知道您当前所在的分支?它保存了一个特殊的指针,称为 HEAD。请注意,这与您可能习惯的其他 VCS(例如 Subversion 或 CVS)中的 HEAD 概念有很大不同。在 Git 中,这是指向您当前所在的本地分支的指针。在这种情况下,你仍然是主人。 git branch 命令只创建了一个新分支——它没有切换到那个分支。


不错,不过可以使用显示分离的 HEAD 外壳的图片
@DonHatch,分离 HEAD stackoverflow.com/a/35301963/1074179 的很好解释
不错的答案。分支只不过是标记的提交,当您进行新的提交时,此标签将移动到新的新提交。当您签出没有标签的提交时,它处于分离的 HEAD 状态。这意味着 HEAD 指向一个没有分支标签的提交。如果您在上面的示例中签出 34ac2,现在 HEAD 将指向该提交,它被称为分离的 HEAD。在这种状态下,您也可以进行更改、试验和提交更改,但是一旦您签出不同的分支,您将丢失所有更改,除非您当然创建了一个新分支。
@sleepwalkerfx 但您可以签出具有分支标签但仍处于分离头状态的提交,因为您的 HEAD 不再指向分支标签而是分支的提交 ID
@sleepwalkerfx 我认为我们现在正在谈论语义。您是正确的,分支标签是对特定提交的文本引用。然而,它不是一个提交。因此,如果您执行 git log 并得到类似 commit ad0265... HEAD -> foo ... 的内容,则意味着 foo 分支是对提交 ID ad0265 的引用。对文本引用 foo 进行检查并不是一个独立的头脑。对提交 ID ad0265 进行检查会导致头部分离。可能是我错过了你所交流的一些微妙之处。我希望这堵文字墙有助于发现我迷路的地方。
R
Rob Bednark

我向 github 开发者 Scott Chacon [video reference] 推荐这个定义:

Head 是您当前的分支。这是一个象征性的参考。它是对分支的引用。你总是有 HEAD,但 HEAD 将指向这些其他指针之一,指向你所在的分支之一。它是您下一次提交的父级。它应该是最后一次签出到您的工作目录的内容...这是您工作目录的最后一个已知状态。

整个视频将公平地介绍整个 git 系统,所以我也建议你如果有时间观看它。


所以真正的定义是“你下一次提交的父级”
还有“指向下一个将移动的分支的东西”
@nicolas - 我不认为这是真的。 HEAD 可以指向任何提交,它不一定必须指向一个分支——当它没有指向时,你处于“分离的 HEAD”模式。
该视频很棒,但不幸的是,它为 Stack Overflow 提供了不合适的答案。如果视频在未来某个时间被删除怎么办?然后您的链接将指向任何内容。一个更好的答案将包括斯科特在视频中所说的文字记录。
Scott 说 HEAD 是指向分支的指针。但是 HEAD 也可以指向旧的提交。这太令人困惑了。
n
nonopolarity

假设它不是称为“分离的 HEAD”的特殊情况,那么,如 O'Reilly Git 书第 2 版第 69 页中所述,HEAD 表示:

HEAD 总是指当前分支上的最新提交。当您更改分支时,HEAD 会更新以引用新分支的最新提交。

所以

HEAD 是当前分支的“尖端”。

请注意,我们可以使用 HEAD 来引用最近的提交,并使用 HEAD~ 作为提示之前的提交,使用 HEAD~~HEAD~2 作为更早的提交,等等。


C
Community

HEAD 指的是您的工作副本指向的当前提交,即您当前已签出的提交。从 official Linux Kernel documentation on specifying Git revisions

HEAD 命名工作树中更改所基于的提交。

但是请注意,在即将发布的 Git 1.8.4 版本中,@ 也可以用作 HEAD 的简写,如 noted by Git contributor Junio C Hamano in his Git Blame blog

你可以说“@”而不是输入“HEAD”,例如“git log @”。

Stack Overflow 用户 VonC 还发现了一些 interesting information on why @ was chosen as a shorthand in his answer to another question

同样有趣的是,在某些环境中,不需要大写 HEAD,特别是在使用不区分大小写文件系统的操作系统中,特别是 Windows 和 OS X。


C
Cody Gray

看看Creating and playing with branches

HEAD 实际上是一个文件,其内容决定了 HEAD 变量所指的位置:

$ cat .git/HEAD
ref: refs/heads/master
$ cat .git/refs/heads/master
35ede5c916f88d8ba5a9dd6afd69fcaf773f70ed

在这个存储库中,HEAD 文件的内容是指名为 refs/heads/master 的第二个文件。文件 refs/heads/master 包含 master 分支上最近提交的哈希。

结果是 HEAD 指向 .git/refs/heads/master 文件中的主分支提交。

https://i.stack.imgur.com/kYkB8.png


u
user3751385

在阅读了所有以前的答案之后,我仍然想要更清楚。 git 官方网站 http://git-scm.com/blog 上的这个博客给了我想要的东西:

HEAD:指向最后提交快照的指针,下一个父级

Git 中的 HEAD 是指向当前分支引用的指针,它又是指向您所做的最后一次提交或检出到工作目录中的最后一次提交的指针。这也意味着它将成为您下一次提交的父级。通常将其视为 HEAD 是您上次提交的快照是最简单的。


HEAD: last commit snapshot, next parent 不准确。 HEAD 不是提交;它指向一个。
无需讽刺;在您进行编辑之前,即使引用是准确的,粗体大字也是一种简化并且有点误导。现在,好多了。
如果您阅读下一行:Git 中的 HEAD 是指向当前分支引用的指针,它又是指向您所做的最后一次提交或检出到工作目录的最后一次提交的指针。 -- 请注意这里使用了“指针”这个词。
虽然“最后提交快照”描述确实给出了 HEAD 通常应该如何使用的概念性感觉,但它确实不准确。如果我进行提交,然后切换到另一个分支,HEAD 不再指向最后一个提交快照。它指向我刚刚切换到的分支上的最后一个提交快照。如果我checkout HEAD^,现在 HEAD 甚至不会指向任何分支上的最后一个提交快照。
“你下一次提交的父级(如果你现在要提交的话)”更准确,但是除了提交之外,git 中还有许多其他操作受 HEAD 影响。真的,归根结底,HEAD 就是 HEAD,其性质取决于它如何影响 commitmergerebaselog 等命令。但从概念上讲,可能是“(指向)当前位置"是一个很好的总结。
m
mike

我只想详细说明 Greg Hewgil 接受的答案中的一些内容。根据 Git Pocket Guide

分支:

分支本身被定义为从命名提交(分支的“尖端”)在提交图中可到达的所有点。

HEAD:一种特殊类型的 Ref

特殊的 ref HEAD 决定了你在哪个分支......

参考文献

Git 定义了两种引用或命名指针,它称之为“refs”: 一个简单的 ref,它直接指向一个对象 ID(通常是一个提交或标签) 一个符号 ref(或 symref),它指向另一个 ref (无论是简单的还是象征性的)

正如 Greg 提到的,HEAD 可以处于“分离状态”。所以 HEAD 可以是简单的 ref(对于分离的 HEAD)或 symref。

如果 HEAD 是现有分支的符号引用,那么你就在那个分支上。另一方面,如果 HEAD 是一个简单的 ref,直接通过其 SHA-1 ID 命名提交,那么您不是“在”任何分支上,而是处于“分离 HEAD”模式,当您之前签出时会发生这种情况承诺审查。


谢谢你,@迈克!这是第一个阐明当您签出较早的提交时会发生什么的答案。查看 git 网站上的 book,我觉得“分离的 HEAD”是一种病态状态,只有在你做了一些奇怪的变基操作时才会进入。但是检查一个较早的提交并不是一件奇怪的事情,当你这样做时,HEAD 不是“当前分支的尖端”。所以这是我第一次觉得我真的明白了。
A
Alexis Määttä Vinkler

Git 中的 HEAD 是什么? (概念上)

HEAD 是指向当前签出的分支或提交的指针,它回答了以下问题:我现在在存储库中的什么位置? 或者,换句话说,它是Git 知道在哪个提交上镜像您的本地工作树的方式,以及您当前是否在分支上工作(attached)或没有(detached)。

分离头

HEAD 可以处于两种状态中的任一种,attacheddetached,具体取决于您是否检查过出一个分支与否。默认状态为 attached,其中对历史记录的任何操作都会自动记录到当前引用的分支 HEAD

在分离状态下,可以在不影响任何现有分支的情况下进行实验性更改。请参阅下面的信息图,说明在附加状态与分离状态下提交之间的区别。

https://i.stack.imgur.com/vaQxa.png

一个常见的误解是消息 You are in 'detached HEAD' state 是错误的,而实际上它只是描述了 HEAD 如何引用当前快照。

可以使 HEAD 处于分离状态的操作:

签出特定的提交,即 $ git checkout 14ko3

显式检出远程分支,即 $ git checkout origin/master

使用 detached 标志切换到分支,即 $ git switch master --detached

签出一个标签,即 $ git checkout v1.0.1

执行交互式变基,或包含冲突更改的常规变基

从分离状态转变为附加状态

要从分离状态转移到附加状态,您可以从您所在的位置创建一个新分支,或者切换回现有分支。

注意:如果您切换到另一个现有分支,则在分离状态下创建的任何提交最终都将被丢弃(垃圾回收后),而无需先将更改保留在新分支中。

检查 HEAD 状态

可以通过不同的方式确定 HEAD 当前处于哪个状态,这里有两个选项。

using show $ git show HEAD --oneline 14ko3 (HEAD, master) C1 # 如果附加,输出将是 14ko3 (HEAD -> master) C1

使用 status $ git status HEAD 在 14ko3 分离

HEAD 到底是什么? (技术上)

如果您想明确查看 HEAD 所引用的内容,您可以随时检查 .git/HEAD 文件,这是 Git 在内部用于管理 HEAD 的实际文件。该文件包含分支或提交哈希的名称,具体取决于 HEAD 是否分离。

$ cat .git/HEAD
ref: refs/heads/master

# If detached, the output would have been
14ko36e295f1a98ec57397b3acc7bc247da61ff5

来源: 以上摘录来自这篇关于该主题的完整帖子:What is HEAD in Git?


嗨,Alexis,你关于 git 的博客是我读过的关于 git 的最鼓舞人心的内容之一,非常感谢!
@Jeff 感谢您的精彩评论,正是这样的反馈不断激励我写更多!我很高兴你发现它很有用。
N
Nataraj

我认为'HEAD'是当前的签出提交。换句话说,“HEAD”指向当前签出的提交。

如果您刚刚克隆并且没有签出我不知道它指向什么,可能是一些无效的位置。


是的,特殊参考 HEAD 是您当前已签出的任何提交。详见the manual(相关段落紧跟在图 3.4 之后)。
如果你克隆一个存储库,git 默认会检出 master 分支——所以 HEAD 将指向 master。
@sleske 如果您克隆没有特殊选项的存储库,git 将检查远程头。它通常是 master,但并非总是如此。见remote set-head
我之前的评论是正确的,除了对 remote set-head 的引用,它只影响本地默认分支,不会更改服务器的默认值。
t
tjb

将正确答案中的观点带回家的一个好方法是运行 git reflog HEAD,您可以获得 HEAD 指出的所有地点的历史记录。


s
stack1

Head 指向当前签出分支的尖端。

https://i.stack.imgur.com/IstVN.png

在您的存储库中,有一个 .git 文件夹。在此位置打开文件:.git\refs\heads。该文件(大多数情况下为 master)中的(sha-1 哈希)代码将是最近的提交,即在命令 git log 的输出中看到的那个。有关 .git 文件夹的更多信息:http://gitready.com/advanced/2009/03/23/whats-inside-your-git-directory.html


当前分支的尖端指向最近的提交是一个常见的误解。通常情况下确实如此,但 git reset HEAD^ 也很常见,然后分支的尖端不再指向最近的提交(前一个提示)。
V
Vertexwahn

感觉 HEAD 只是您签出的最后一次提交的标签。

这可以是特定分支的尖端(例如“master”)或某个分支的中间提交(“分离头”)


A
Anwar Husain

除了所有定义之外,我脑海中浮现的一件事是,当您进行提交时,GIT 会在存储库中创建一个提交对象。提交对象应该有一个父对象(如果是合并提交,则应该有多个父对象)。现在,git 如何知道当前提交的父级?所以 HEAD 是指向(引用)最后一次提交的指针,它将成为当前提交的父级。


S
S R Chaitanya

Git 都是关于提交的。
Head 指向您当前签出的提交。

$ git cat-file -t HEAD
commit

每当您签出一个分支时,HEAD 都会指向该分支上的最新提交。 HEAD 的内容可以检查如下(对于 master 分支):

$ cat .git/refs/heads/master
  b089141cc8a7d89d606b2f7c15bfdc48640a8e25

J
Jerry Chen

我还在弄清楚 git 的内部结构,并且到目前为止已经弄清楚了:

假设当前分支是master。

HEAD 是 .git/ 目录中的一个文件,通常看起来像这样:

% cat .git/HEAD
ref: refs/heads/master

refs/heads/master 本身就是一个文件,通常具有 master 最新提交的哈希值:

% cat .git/refs/heads/master 
f342e66eb1158247a98d74152a1b91543ece31b4

如果你做一个 git log,你会看到这是 master 的最新提交:

% git log --oneline 
f342e66 (HEAD -> master,...) latest commit
fa99692 parent of latest commit

所以我的想法是 HEAD 文件是一种跟踪最新提交的便捷方式,而不是记住长哈希值。


G
Goma

HEAD 几乎是分支的负责人。所以当你观察一个分支时,你看到的是最新的提交,也就是这个分支的头。但是,您可以指出自己正在查看该分支历史中更早的另一个提交,并且当您这样做时,您正在将 HEAD 移动到先前的提交。由于 HEAD 自然属于分支中的最新提交,因此它被认为是分离的。

一种视觉表现。每个分支都是毛毛虫,每次提交都是生物的一部分。因此,HEAD 将位于最领先的部分。如果您将 HEAD 从该段移至另一个要使用的段,则您已将头与自然段分离。希望它有任何意义。

现在,如果您在主分支中分离 HEAD,然后签出 newFeature,然后再次签出 main,HEAD 仍将被分离,并在另一个提交之上。我将 HEAD 视为一面镜子,您可以将其指向您想要的位置。


M
Marcus Thornton

这两个可能会让你感到困惑:

指向最近提交的分支的命名引用。除非您使用包引用,否则头通常存储在 $GIT_DIR/refs/heads/ 中。

当前分支,或者您的工作树通常是从 HEAD 指向的树生成的。 HEAD 必须指向一个头,除非您使用的是分离的 HEAD。


T
Ting Wang

看看http://git-scm.com/book/en/Git-Branching-What-a-Branch-Is

图 3-5。 HEAD 文件指向你所在的分支。


Stackoverflow 上通常不赞成仅链接的答案,请在您的答案中内嵌相关信息。
这并不完全正确。 HEAD 所指的内容取决于您是在谈论裸仓库还是非裸仓库。在非裸仓库的上下文中,它实际上是指当前签出的提交,它不需要附加分支(即处于分离 HEAD 状态时)。
O
Okd

分支实际上是一个指针,其中包含一个提交 ID,例如 17a5。 HEAD 是指向用户当前正在处理的分支的指针。

HEAD 有一个参考文件,如下所示:

参考:

您可以通过访问您正在使用的存储库中的 .git/HEAD .git/refs 检查这些文件。


M
Mahdi mehrabi

HEAD 实际上只是一个用于存储当前分支信息的文件

如果你在你的 git 命令中使用 HEAD 你指向你当前的分支

您可以通过 cat .git/HEAD 查看此文件的数据


F
Faizan Makandar

存储库中可以有多个头。并且 head 的总数始终等于存储库中存在的分支总数,这意味着 head 只不过是每个分支的最新提交

但是存储库只有一个 HEAD。HEAD 是一个引用,它引用在当前分支完成的最新提交。

它就像 git 用户的眼睛。无论 HEAD 引用哪个提交,存储库都会开始反映存储库在该特定提交期间所具有的条件。

HEAD 的基本性质是始终引用当前分支的最新提交,但我们可以使用 git checkout "commit-hash" 将 HEAD 移动到当前分支的任何提交

注意:我们可以使用命令 git log --oneline 轻松获取 commit-hash


d
dukeofgaming

作为一个概念,头部是分支中的最新版本。如果每个命名分支有多个头,则可能在进行本地提交而不合并时创建了它,从而有效地创建了一个未命名的分支。

要拥有一个“干净”的存储库,每个命名分支应该有一个头,并且在本地工作后总是合并到一个命名分支。

Mercurial 也是如此。


Mercurial 是这样,但 Git 不是这样。