Git中的HEAD是什么?

1317

您可以看到Git文档中说:

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

但是Git中的HEAD究竟是什么?


11
请参考https://dev59.com/h3NA5IYBdhLWcg3wbNQ4#967611。 - VonC
有趣的是,“heads”和(命名的)“branches”之间几乎存在一对一的对应关系(请参见git ls-remote),但“HEAD”的定义似乎很难确定。git branch的文档简单地说明HEAD实际上是“当前分支的末端”。所有的混淆实际上都必须关于分支的定义 - 这是令人惊讶的微妙:我们到底是什么意思“branch”? - Brent Bradburn
_HEAD指的是你在工作树中所做更改所基于的提交。来源:gitrevisions manual - David Balažic
27个回答

15

我只想详细说明Greg Hewgil的回答中的一些内容。根据Git Pocket Guide所述:

分支:

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

HEAD:一种特殊类型的引用

特殊引用HEAD确定您所在的分支...

引用

Git定义了两种引用或命名指针,称之为“refs”:

  • 一个简单的引用,直接指向对象ID(通常是提交或标签)
  • 一个符号引用(或symref),指向另一个引用(无论是简单还是符号)

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

如果HEAD是现有分支的符号引用,则您“在”该分支上。另一方面,如果HEAD是直接使用其SHA-1 ID命名提交的简单引用,则您不在任何分支上,而是处于“分离的HEAD”模式下,这种情况发生在您检出某个早期提交以进行审查时。

1
谢谢您,@Mike!这是第一个澄清当您检出早期提交时会发生什么的答案。在Git网站的 书籍 上看到,“分离的 HEAD”似乎只有在进行一些奇怪的变基操作时才会进入病态状态。但是,检出较早提交并不是一件奇怪的事情,当您执行此操作时,HEAD 不是“当前分支的顶端”。因此,这是我第一次感觉真正理解了。 - Nat Kuhn

7

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

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


是的,特殊引用 HEAD 指的是当前所检出的提交。有关详细信息,请参见手册(相关段落紧随图3.4之后)。 - Calrion
1
如果你克隆一个代码库,git默认会检出master分支 - 因此HEAD将指向master。 - sleske
1
如果没有特殊选项克隆存储库,则git将检出远程头。它通常是master,但并不总是。请参阅remote set-head - De Novo
我的先前评论是正确的,除了对remote set-head的引用,它只影响本地默认分支,并不会更改服务器上的默认分支。 - De Novo

6
运行git reflog HEAD可以获取HEAD指向的所有位置的历史记录,这是加深正确答案观点的好方法。

5

Head指向当前检出分支的末尾。

enter image description here

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


3
有一个常见的误解,即当前分支的末尾指向最近的提交。通常情况下确实是这样的,但是使用git reset HEAD^命令将取消指向最近提交的指针,此时分支末尾不再指向原来的最新提交。 - LarsH

4

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

这可以是特定分支(如“master”)的末尾,也可以是分支的某个中间提交(“脱离HEAD”)。


3

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

一个视觉表现。每个分支都像毛毛虫一样,每个提交都是这个生物的一部分。因此,HEAD 将位于最前面的那个部分。如果你将 HEAD 从那里移除到另一个部分中使用,你就将头部从自然部分中分离出来了。希望这有任何意义。

现在,如果你在主分支中分离 HEAD,然后检出 newFeature,再次检出主分支,那么 HEAD 仍将是分离的,并位于另一个提交之上。我认为 HEAD 就像是一个可以指向你想要的地方的镜子。


2
了解 HEAD 是什么,让我们通过两个不同开发者 Dev1 和 Dev2 在同一项目上使用两个不同分支 main 和 feature1 的实际例子来说明。
第一种情况是 Dev1 的 main 分支:
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

下一个1

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

下一个2

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

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

现在让我们进入Next1步骤,使用cd命令进入refs目录,并在heads目录中搜索main文件并查看其内容。

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 分支

在这里,唯一的区别将在 Next2 步骤中出现

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

还有以下内容:

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

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

结论:

HEAD指的是当前提交而不是当前分支。只是恰巧包含该当前提交的文件被命名为当前分支,我们倾向于说“HEAD指的是当前分支”。


2
除了所有的定义,让我印象深刻的是,当你提交代码时,GIT会在仓库中创建一个提交对象。提交对象应该有一个父对象(如果是合并提交,则可能有多个父对象)。那么GIT如何知道当前提交的父对象呢?所以HEAD是指向最后一个提交的指针,它将成为当前提交的父对象的引用。

2

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

但是一个仓库只有一个HEAD。HEAD是一个引用,它引用了当前分支上最新的提交。

就像Git用户的眼睛一样。无论HEAD引用哪个提交,仓库都开始反映仓库在该特定提交期间的状态。

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

注意:我们可以使用命令git log --oneline轻松获取提交哈希。


2

Git 是关于提交的。
Head 指向你当前检出的提交。

$ git cat-file -t HEAD
commit

无论何时你检出一个分支,HEAD指向该分支上最新的提交。可以如下检查HEAD的内容(对于主分支):
$ cat .git/refs/heads/master
  b089141cc8a7d89d606b2f7c15bfdc48640a8e25

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接