2023-04-02 05:00:03

Git中的HEAD是什么?

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

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

但Git 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文件。

其他回答

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

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

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

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

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

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

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

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

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

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

什么是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会。

为了理解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指向当前分支'。