2023-04-02 05:00:03

Git中的HEAD是什么?

您可以看到Git文档中这样说

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

但Git HEAD到底是什么?


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

你可以通过这样做来查看HEAD指向什么:

cat .git/HEAD

在我的例子中,输出是:

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

HEAD可以引用与分支名称不关联的特定修订。这种情况被称为分离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的内部工作原理(因此可以更好地理解heads/HEAD)。引用(ref:)或头或分支可以被看作是粘贴在提交历史记录中的提交上的便利贴。通常它们指向一系列提交的提示,但它们可以随着git checkout或git reset等移动。


我推荐github开发者Scott Chacon的定义:

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

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


作为一个概念,头是分支中的最新修订。如果每个命名分支有多个头,那么在进行本地提交时可能会创建它,而没有合并,这实际上创建了一个未命名的分支。

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

Mercurial也是如此。


要把正确答案中的要点讲清楚,一个很好的方法就是跑步 git reflog HEAD,你会得到HEAD所指向的所有地方的历史。


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

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


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

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


HEAD指的是工作副本所指向的当前提交,即当前签出的提交。从官方Linux内核文档中指定Git修订:

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

但是请注意,在即将到来的Git 1.8.4版本中,@也可以用作HEAD的简写,Git贡献者Junio C Hamano在他的Git Blame博客中指出:

你可以不说“HEAD”,而说“@”。“git log @”。

Stack Overflow用户VonC在回答另一个问题时还发现了一些有趣的信息,关于为什么选择@作为简写。

同样值得注意的是,在某些环境中,没有必要将HEAD大写,特别是在使用不区分大小写文件系统的操作系统中,特别是Windows和OS X。


我想详细介绍一下格雷格·休吉尔公认答案中的一些内容。 根据Git口袋指南

分支机构:

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

HEAD:一种特殊类型的Ref

特殊参考HEAD决定你在哪个分支上…

Refs

Git定义了两种引用或命名指针,并调用它们 “参考文献”: 一个简单的引用,直接指向一个对象ID(通常是一个提交或标记) 一个符号ref(或symref),指向另一个ref(简单的或符号的)

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

如果HEAD是一个现有分支的符号引用,那么你是“开启”的。 分支。另一方面,如果HEAD是直接的简单引用 通过SHA-1 ID命名一个提交,那么你不在任何分支上,但是 而是在“分离HEAD”模式下,当你查看一些内容时就会发生这种情况 早交审查。


这两个可能会让你困惑:

head

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

HEAD

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


看一下如何创建和使用分支

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

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

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

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


假设这不是一种叫做“分离HEAD”的特殊情况,那么,正如O'Reilly Git书第2版第69页所述,HEAD的意思是:

HEAD总是指当前节点上最近的提交 分支。当您更改分支时,HEAD将被更新为引用新的分支 布兰奇的最新提交。

so

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

注意,我们可以使用HEAD表示最近的提交,使用HEAD~表示提示之前的提交,使用HEAD~~或HEAD~2表示更早的提交,以此类推。


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

HEAD:指向最后一次提交快照的指针

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


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: $ git branch testing This creates a new pointer at the same commit you’re currently on 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.


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

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


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

这可以是一个特定分支的顶端(例如“master”),也可以是某个分支的中间提交(“detached head”)。


除了所有的定义之外,让我印象深刻的是,当你进行提交时,GIT会在存储库中创建一个提交对象。提交对象应该有一个父对象(如果是合并提交则有多个父对象)。现在,git如何知道当前提交的父节点?因此HEAD是一个指针,指向最后一次提交(引用),它将成为当前提交的父对象。


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

什么是HEAD?

头就是你

HEADis a symbolic reference pointing to wherever you are in your commit history. It follows you wherever you go, whatever you do, like a shadow. If you make a commit, HEAD will move. If you checkout something, HEAD will move. Whatever you do, if you have moved somewhere new in your commit history, HEAD has moved along with you. To address one common misconception: you cannot detach yourself from HEAD. That is not what a detached HEAD state is. If you ever find yourself thinking: "oh no, i'm in detached HEAD state! I've lost my HEAD!" Remember, it's your HEAD. HEAD is you. You haven't detached from the HEAD, you and your HEAD have detached from something else.

HEAD可以附着在什么上?

HEAD可以指向提交,但通常不会。让我再说一遍。通常,HEAD并不指向提交。它指向一个分支引用。它被附加到那个分支,当你做某些事情时(例如,提交或重置),附加的分支将随着HEAD一起移动。您可以通过查看引擎盖下面的内容来查看它所指向的内容。

cat .git/HEAD

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

ref: refs/heads/master

有时候你会得到这样的结果:

a3c485d9688e3c6bc14b06ca1529f0e78edd3f86

That's what happens when HEAD points directly to a commit. This is called a detached HEAD, because HEAD is pointing to something other than a branch reference. If you make a commit in this state, master, no longer being attached to HEAD, will no longer move along with you. It does not matter where that commit is. You could be on the same commit as your master branch, but if HEAD is pointing to the commit rather than the branch, it is detached and a new commit will not be associated with a branch reference.

如果您尝试下面的练习,您可以图形化地看待这个问题。从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仍然引用与分支名称相关联的特定修订。我们还在主分支上,不是吗?

现在试一试:

git status
# HEAD detached at a3c485d

没有。我们处于“分离的头部”状态。

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

总之

HEAD就是你。无论你在哪里,它都指向你所办理的任何手续。通常这不是一个提交,而是一个分支。如果HEAD确实指向一个提交(或标记),即使它是一个分支也指向的同一个提交(或标记),您(和HEAD)已经从那个分支中分离出来了。由于您没有附加到您的分支,所以当您进行新的提交时,该分支不会跟随您。然而,HEAD会。


分支实际上是一个持有提交ID(例如17a5)的指针。 HEAD是指向用户当前工作的分支的指针。

HEAD有一个如下所示的引用流:

ref:

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


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

$ git cat-file -t HEAD
commit

无论何时签出一个分支,HEAD都指向该分支上的最新提交。HEAD的内容可检查如下(主分支):

$ cat .git/refs/heads/master
  b089141cc8a7d89d606b2f7c15bfdc48640a8e25

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

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

您可以通过查看该文件的数据 猫. /头


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

假设当前的分支是master。

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

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

Refs /heads/master本身是一个文件,通常包含master最新提交的哈希值:

% cat .git/refs/heads/master 
f342e66eb1158247a98d74152a1b91543ece31b4

如果你做git日志,你会看到这是master的最新提交:

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

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


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

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

分离的头

HEAD可以处于附加或分离两种状态之一,这取决于是否签出了分支。默认状态是附加的,其中对历史的任何操作都会自动记录到HEAD当前引用的分支。

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

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

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

签出一个特定的提交,即。 $ git checkout 14ko3 显式地签出一个远程分支,即。 $ git checkout origin/master 使用分离标志切换到一个分支,即。 $ git主开关——分离 签出一个标签,即 $ git checkout v1.0.1 执行交互式重基,或包含冲突更改的常规重基

从分离到依附的状态

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

注意:如果你切换到另一个现有的分支,在分离状态下创建的任何提交最终(垃圾收集后)都会被丢弃,而不是首先在一个新的分支中持久化你的更改。

检查HEAD的状态

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

使用显示 $ git show HEAD—一行 14ko3 (HEAD, master) C1 #如果附加,输出将是 14ko3 (HEAD -> master) C1 使用状态 $ git状态 HEAD在14ko3分离

HEAD到底是什么?(技术)

如果您想显式地查看HEAD引用了什么,您可以检查. Git /HEAD文件,这是Git内部用于管理HEAD的实际文件。该文件包含分支的名称或提交散列,这取决于是否分离HEAD。

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

# If detached, the output would have been
14ko36e295f1a98ec57397b3acc7bc247da61ff5

来源:以上节选自这篇关于主题的完整文章:Git中的HEAD是什么?


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

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

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

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

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


HEAD几乎字面上就是分支的头。所以当你观察一个分支时,你看到的是最新的提交,也就是这个分支的头。但是,您可以将自己指向该分支历史上更远的另一个提交,当您这样做时,您是在将HEAD移动到前一个提交。由于HEAD自然属于分支中的最新提交,因此它被认为是分离的。

视觉表现。每一根树枝都是一条毛毛虫,每一根树枝都是这个生物的一个部分。因此HEAD将位于最前面的段中。如果您将HEAD从该部分移除到另一个要使用的节段,则您已经将HEAD从自然节段中分离出来。希望你能明白。

现在如果你在主分支中分离HEAD,然后签出newFeature,然后再次签出main, HEAD仍然会被分离,并且是在另一个提交之上。我把HEAD看作一面镜子,你可以把它指向你想要的地方。


我自己还没有弄清楚,但Silfheed对“贴在节点上的便利贴”定义的链接是迄今为止我发现的最好的链接。

我想分享一下我对寻找定义的印象,否则我为什么要把它留给自己。 我把它理解为粘在Git树空间当前位置上的贴纸。我错过了这样一个术语——GIT树中的当前位置,它有自己的属性来指示我们所处的位置。

“参考”或“指针”之类的概念我不太清楚。在我看来,它们暗示了一种新的抽象层次,我们从抽象层次的内部来“指”某物。这可能不是真的,但到目前为止,这是我的看法。


为了理解HEAD是什么,让我们使用一个实际的例子,其中有两个不同的分支,main和feature1,来自两个不同的开发人员Dev1和Dev2,他们在同一个项目上工作。

Dev1主分支的案例1:

name~/repo (main)
$ ls -al
total 9
drwxr-xr-x 1 AlphaLy 197121  0 Dec 22 15:09 ./
drwxr-xr-x 1 AlphaLy 197121  0 Dec 21 20:35 ../
drwxr-xr-x 1 AlphaLy 197121  0 Dec 22 15:09 .git/
-rw-r--r-- 1 AlphaLy 197121 20 Dec 21 20:35 README.md
-rw-r--r-- 1 AlphaLy 197121  0 Dec 22 15:09 test.txt

Next1

name~/repo (main) 
$ cd .git/
name~/repo/.git (GIT_DIR!)
$ ls
COMMIT_EDITMSG  description  HEAD    index  logs/     ORIG_HEAD    refs/
config          FETCH_HEAD   hooks/  info/  objects/  packed-refs

Next2

name~/repo/.git (GIT_DIR!)
$ cat HEAD
ref: refs/heads/main

我们可以清楚地看到HEAD是一个文件,其中一行指向refs/heads目录中的一个名为main的文件(heads是refs目录中的一个目录)。参考Next1步骤定位refs目录

现在让我们转到Next1步骤,cd到refs目录,在heads目录中搜索主文件,看看它包含什么

name~/repo/.git (GIT_DIR!)
    $ cd refs
    
name~/repo/.git/refs (GIT_DIR!)
    $ ls
    heads/  remotes/  tags/
    
name~/repo/.git/refs/ (GIT_DIR!)
    $ cd heads/
    
name~/repo/.git/refs/heads (GIT_DIR!)
    $ ls
    main  maxi

name~/repo/.git/refs/heads (GIT_DIR!)
$ cat main
8b25d89f3396177416c055ab07ebf778616eecdd

8b25d89f3396177416c055ab07ebf778616eecdd是当前提交。因此,我们可以说HEAD指向一个名为main的文件(总是以当前分支命名),该文件包含当前提交(上面的40位字符)。HEAD指的是当前提交。

带有Dev2的feature1分支的案例2

在这里,唯一的区别是在Next2步骤中

name~/repo/.git (GIT_DIR!)
$ cat HEAD
ref: refs/heads/feature1

以及以下几点:

    name~/repo/.git (GIT_DIR!)
   $ cat feature1
03fbf973ac1014085864234010b82393208ebbd6

03fbf973ac1014085864234010b82393208ebbd6是在feature1分支上的当前提交。因此,这里HEAD再次指向一个名为feature1的文件(以当前分支命名),该文件包含当前提交(上面的40位字符)。

结论:

HEAD是指当前提交而不是当前分支。这只是一个巧合,包含当前提交的文件以当前分支命名,我们倾向于说'HEAD指向当前分支'。