Git中的HEAD是什么?


What is HEAD in Git?

你看到Git文档说的是

The branch must be fully merged in HEAD.

但Git-HEAD到底是什么?


你可以把头部想象成"当前的分支"。当使用git checkout切换分支时,头部修订将更改为指向新分支的尖端。

您可以通过执行以下操作看到head指向什么:

1
cat .git/HEAD

在我的例子中,输出是:

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

head可以引用与分支名称无关的特定修订。这种情况被称为分离式头部。


引用他人:

A head is simply a reference to a
commit object. Each head has a name
(branch name or tag name, etc). By
default, there is a head in every
repository called master. A repository
can contain any number of heads. At
any given time, one head is selected
as the"current head." This head is
aliased to HEAD, always in capitals".

Note this difference: a"head"
(lowercase) refers to any one of the
named heads in the repository;"HEAD"
(uppercase) refers exclusively to the
currently active head. This
distinction is used frequently in Git
documentation.

这里可以找到另一个很好的资源,它可以快速地覆盖git的内部工作(从而更好地理解头/头)。引用(ref:)或头或分支可以被视为提交历史记录中粘在提交上的post-it注释。通常,它们指向一系列提交的尖端,但它们可以与git checkoutgit reset等一起移动。


我建议Github开发者Scott Chacon[视频参考]:

Head is your current branch. It is a symbolic reference. It is a reference to a branch. You always have HEAD, but HEAD will be pointing to one of these other pointers, to one of the branches that you're on. It is the parent of your next commit. It is what should be what was last checked-out into your working directory... This is the last known state of what your working directory was.

整个视频将公平地介绍整个Git系统,因此我也建议您在有时间的情况下观看所有内容。


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

从pro-git手册的第3.1章git分支-简而言之,在创建新分支一节中:

What happens if you create a new branch? Well, doing so creates a new
pointer for you to move around. Let’s say you create a new branch
called testing. You do this with the git branch command:

1
$ git branch testing

This creates a new pointer at the same commit you’re currently on

enter image description here

How does Git know what branch you’re currently on? It keeps a special pointer called HEAD. Note that this is a lot different than
the concept of HEAD in other VCSs you may be used to, such as
Subversion or CVS. In Git, this is a pointer to the local branch
you’re currently on. In this case, you’re still on master. The git
branch command only created a new branch — it didn’t switch to that
branch.

enter image description here


假设这不是一个称为"分离头"的特殊情况,那么,正如O'Reilly Git书第二版第69页所述,HEAD意味着:

HEAD always refers to the most recent commit on the current
branch. When you change branches, HEAD is updated to refer to the new
branch’s latest commit.

所以

HEAD is the"tip" of the current branch.

请注意,我们可以使用HEAD来指代最新的提交,使用HEAD~作为TIP之前的提交,使用HEAD~~HEAD~2作为甚至更早的提交,等等。


HEAD是指您的工作副本指向的当前提交,即您当前已签出的提交。从有关指定git修订版的官方Linux内核文档中:

HEAD names the commit on which you based the changes in the working tree.

然而,请注意,在即将到来的Git 1.8.4版本中,正如Git贡献者Junio C Hamano在其Git责备博客中指出的那样,@也可以用作HEAD的简写:

Instead of typing"HEAD", you can say"@" instead, e.g."git log @".

堆栈溢出用户vonc还发现了一些有趣的信息,说明为什么在回答另一个问题时选择@作为速记。

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


看看创建和使用分支

head实际上是一个文件,其内容决定head变量引用的位置:

1
2
3
4
$ cat .git/HEAD
ref: refs/heads/master
$ cat .git/refs/heads/master
35ede5c916f88d8ba5a9dd6afd69fcaf773f70ed

在此存储库中,头文件的内容引用名为refs/heads/master的第二个文件。文件refs/heads/master包含主分支上最近提交的哈希。

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

enter image description here


我只想在格雷格·休吉尔接受的回答中详细说明一些事情。根据Git袖珍指南

分支机构:

the branch itself is defined as all points reachable in the commit
graph from the named commit (the"tip" of the branch).

头部:特殊类型的参考

The special ref HEAD determines what branch you are on...

参考文献

Git defines two kinds of references, or named pointers, which it calls
"refs":

  • A simple ref, which points directly to an object ID (usually a commit or tag)
  • A symbolic ref (or symref), which points to another ref (either simple or symbolic)

正如格雷格所说,头部可以处于"分离状态"。所以head可以是简单的ref(对于分离的head)或symref。

if HEAD is a symbolic ref for an existing branch, then you are"on"
that branch. If, on the other hand, HEAD is a simple ref directly
naming a commit by its SHA-1 ID, then you are not"on" any branch, but
rather in"detached HEAD" mode, which happens when you check out some
earlier commit to examine.


在这些答案中,有一些可能是微妙但重要的误解。我想我应该加上我的答案来澄清它。

What is HEAD?

号头是你

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

头部可以连接到什么?

HEAD可以指向提交,是的,但通常不会。我再说一遍。通常情况下,HEAD并不指向提交。它指向一个分支引用。它与该分支相连,当您执行某些操作(例如,commitreset时,所连接的分支将与HEAD一起移动。你可以从引擎盖下面看到它指向什么。

1
cat .git/HEAD

通常你会得到这样的结果:

1
ref: refs/heads/master

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

1
a3c485d9688e3c6bc14b06ca1529f0e78edd3f86

HEAD直接指向一个commit时,就会发生这种情况。这被称为分离头,因为HEAD指向的不是分支引用。如果你在这种状态下作出承诺,那么不再依附于HEADmaster将不再与你一起移动。在哪里提交并不重要。您可以与主分支处于同一提交状态,但如果HEAD指向的是提交而不是分支,则它是分离的,并且新提交不会与分支引用关联。

如果您尝试下面的练习,您可以以图形方式查看这个问题。从Git存储库中运行这个。你会得到一些稍微不同的东西,但它们的关键部分会在那里。当需要直接签出提交时,只需使用从第一个输出中得到的任何缩写散列(这里是a3c485d)。

1
2
3
4
5
6
7
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仍指与分支名称关联的特定修订。我们还在总分部,是吗?

现在试试:

1
2
git status
# HEAD detached at a3c485d

不。我们处于"超然状态"。

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

总结

HEAD是你。它指的是无论你在哪里结帐。通常这不是提交,而是分支。如果HEAD确实指向commit(或tag),即使分支也指向相同的commit(或tag),您(和HEAD已经从该分支分离。由于您没有分支连接到您,因此在您作出新的提交时,分支不会跟随您。但是,HEAD会的。


我认为"head"是当前的签出提交。换句话说,"head"指向当前签出的提交。

如果您刚刚克隆了,但没有签出,我不知道它指向什么,可能是某个无效的位置。


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

enter image description here

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


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

head:指向上一个提交快照的指针,下一个父级

The HEAD in Git is the pointer to the current branch reference, which is in turn a pointer to the last commit you made or the last commit that was checked out into your working directory. That also means it will be the parent of the next commit you do. It's generally simplest to think of it as HEAD is the snapshot of your last commit.


开车回家的一个好方法是跑git reflog HEAD,你可以了解到头所指的所有地方的历史。


感觉好像HEAD只是您签出的最后一个提交的标记。

这可以是特定分支(如"master")的提示,也可以是分支提交之间的某些提示("分离的头")。


看看http://git-scm.com/book/en/git-branching-what-a-branch-is

Figure 3-5. HEAD file pointing to the branch you’re on.


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


这两个可能会让你困惑:

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

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


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

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

这也适用于Mercurial。