git中的HEAD是什么?

264

看起来最后一次提交(last commit)、HEAD 和我在目录中看到的文件状态之间似乎存在差异。

什么是 HEAD,我能用它做什么,以及应该避免哪些错误?


4
请参见https://dev59.com/h3NA5IYBdhLWcg3wbNQ4#964927。关于Git中的HEAD和ORIG_HEAD的问题。 - VonC
2
从Git v1.8.4开始,所有使用HEADhead的答案现在可以使用@代替HEAD。请参见此答案(最后一节)以了解为什么可以这样做。 - user456814
3
在Git中,HEAD是指向当前分支引用的指针,而这个引用又是指向你最后一次提交或者检出到你的工作目录中的最后一次提交的指针。这也意味着它将成为你下一次提交的父提交。通常来说,简单地认为HEAD是你最后一次提交的快照即可。 - Quazi Irfan
3
可能是Git中HEAD是什么?的重复问题。 - GeorgeButter
6个回答

206

HEAD指的是当前检出的分支中最近的提交。


这里有一个小例外,那就是"detached HEAD"。当你检出一个提交(或标签),而不是一个分支时,就会出现"detached HEAD"的情况。在这种情况下,你可以将其想象成没有名称的"临时分支";所以我们只有HEAD,而没有命名的分支引用。它仍然允许您进行提交(这将更新HEAD),因此如果您将"detached HEAD"视为没有名称的临时分支,则上述简短的定义仍然是正确的。


1
那么为什么你可以有两个头? - Bite code
1
@e-satis:有时你会看到分支被称为“heads”——它们存储在refs/heads中。小写的head与HEAD不同。我的回答稍微澄清了一下这一点。 - Cascabel
8
这不是正则表达式。^ 只是 Git 表示“前一个提交”的符号,指的是当前提交的前一个提交。如果当前提交是一个合并提交,则使用第一个父提交。 - Cascabel
1
@e-satis:有关指定修订版本的更多信息,请参阅git-rev-list手册页中的指定修订版本部分-这只是一个微小的部分。http://www.kernel.org/pub/software/scm/git/docs/git-rev-parse.html#_specifying_revisions - Cascabel
2
分支只是指向提交的指针,这使得该提交及其所有父提交包含在该分支上。但是,在提交中没有任何东西将它们链接到分支。该链接仅存在于相反方向。因此,当您检出提交时,您无法确定可能指的是哪个分支——它可能是一个分支,可能是所有分支,甚至可能是没有分支。 - poke
显示剩余10条评论

90

HEAD 是指当前检查出来的提交的引用(reference)。

在正常状态下,它实际上是对你所检出的分支的符号引用 - 如果你查看 .git/HEAD 的内容,你会看到类似于 "ref: refs/heads/master" 的东西。分支本身是对分支末端的提交的引用。因此,在正常状态下,HEAD 实际上是指当前分支末端的提交。

也可能存在“分离的 HEAD” 的情况。这发生在你检出的不是(本地)分支,比如远程分支、特定的提交或标签时。最常见的情况是交互式 rebase 过程中,当你选择编辑提交的时候。在分离的 HEAD 状态下,你的 HEAD 直接引用一个提交 - .git/HEAD 的内容将是 SHA1 值。

一般来说,HEAD 只是一个方便的名称,表示“你已经检出的内容”,你并不需要太过担心它。只需了解你所检出的内容,并记住如果你没有在分支上(处于分离的 HEAD 状态),则可能不想提交,除非你知道自己在做什么(例如正在进行交互式 rebase)。


6
为什么在检出远程分支时,会出现“detached HEAD”状态?为什么不会自动切换到对应远程分支的本地分支上?我不太理解这一点。 - Bite code
3
如果你想要本地分支,就要检出本地分支。记住这两者不一定相同——你必须告诉本地分支合并远程分支 (或拉取)。跟踪只是为了让Git知道在请求时自动拉取哪个分支。它未关联的原因是远程分支旨在成为指向远程库中分支的最后位置的指针。如果你试图向其提交更改,那么远程库不会变化,所以远程分支也不应该发生变化。 - Cascabel
1
好的,这就是我没有理解的地方:本地分支的命名方式并不意味着它与远程分支相同。一开始很难理解,因为我来自SVN背景 :-) 谢谢你。顺便问一下,如何将一个无头HEAD移动到本地分支以在此提交? - Bite code
3
通用回答是 git rebase <branch> HEAD。这将找到 <branch>HEAD 的最后一个共同祖先,然后取出从那里到 HEAD 的所有提交,并将它们应用(rebase)到 <branch> 上。它本质上是通过将它们作为补丁来应用它们的,因此如果两个分支非常不同,则可能会产生冲突。但是如果 <branch>HEAD 的祖先(即你在正确的位置,只是忘记了你已经分离了 HEAD),则 rebase 就是一个快进式合并。 - Cascabel
3
这是我找了一段时间后看到的最清晰、最准确的git HEAD解释之一。 - LarsH
显示剩余3条评论

70
我一直以为HEAD~5意味着回到前面5个提交。但它并不包含命令中的“GO”部分,只包含命令中的参考部分。
根据所选择的命令,您可以对该参考进行不同的操作。
通俗地说,它用于回答问题:我应该去哪里?去哪个提交?
  • HEAD表示(对)当前提交的引用
  • HEAD~1表示(对)前一个提交的引用
  • HEAD~也表示(对)前一个提交的引用
  • HEAD~87表示(对)前87个提交的引用
  • HEAD~3..HEAD表示从前3个提交到当前提交(总共3个提交)

使用方法:

  • git checkout HEAD~1 实际上会回退到前一个提交/引用
  • git reset HEAD~3 会取消你最近的3个提交 - 但不会删除更改,也就是说你可以查看最近3个提交中的所有更改,删除你不喜欢的任何内容,或者添加内容然后再次提交它们。
  • git reset --hard HEAD~3 会取消你最近的提交并删除其更改。它会完全删除这些更改。更多信息请参见这里
  • git diff HEAD~3 查看最近3个提交的更改
  • git diff someFile HEAD~3 查看特定文件最近3个更改
  • git revert --no-commit HEAD~3..HEAD。撤销3个提交,但不会自动提交,即你需要自己执行git commit -m。更多信息请参见这里
  • git rev-parse HEAD~2 输出两个提交之前的SHA。
  • 查看拉取请求和主分支之间的所有更改。这是一个多步骤的过程。
git fetch origin/feature22
git checkout feature22
git merge-base feature22 main # will return the SHA of their 050dc022f3a65bdc78d97e2b1ac9b595a924c3f2
git reset 050dc022f3a65bdc78d97e2b1ac9b595a924c3f2

你可以直接执行git reset main,但这只适用于你的同事在拉取请求中创建时拥有最新的主分支更改。如果他们正在开发一个大型功能(并且已经有几天没有与主分支合并),而你只想查看他们在上次从主分支拉取后的新增内容,那么你需要按照上述步骤操作。
确保你也看到了关于什么是分离的HEAD的答案。
这个答案里有一些关于cat .git/HEAD的好信息。

超出范围,但非常有趣:

除了HEAD之外,还有其他类型的头。请参阅git revisions

ORIG_HEAD

ORIG_HEAD是由将您的HEAD在重大方式下移动的命令创建的,以记录在其操作之前HEAD的位置,以便您可以轻松将分支的尖端更改回运行它们之前的状态

要撤消git merge

git reset --hard ORIG_HEAD

MERGE_HEAD

我在尝试合并之前再次合并时遇到了这个错误信息,这是因为我没有完成一个已存在的合并。

致命错误:您尚未完成合并(MERGE_HEAD存在)。 请在合并之前提交您的更改。

为了解决这个问题,我必须完成我的合并,然后再进行另一个合并。

FETCH_HEAD

记录了您最后一次使用git fetch命令从远程仓库获取的分支。

CHERRY_PICK_HEAD

记录了您在运行git cherry-pick时正在挑选的提交。

有关更多信息,请参阅这个其他答案文档


更多官方解释

强烈推荐查看文档中的this页面。

Git作为一个系统,在正常操作中管理和操作三棵树。(在这里,“树”实际上指的是“文件集合”,而不是特定的数据结构。)

HEAD的作用是:

树 角色 我的解释 头部 最后提交的快照,下一个[未来]父节点 在石头中存储的东西。完全记录。将成为下一个记录/提交的父记录。 索引 提议的下一个提交快照 在纸上存储的东西。很可能永久记录在石头中。您可以删除/更改您的纸张。与存储在石头中的记录/提交不同,如果您删除或更改纸张,则没有记录其先前状态的记录。 工作目录 沙盒 一个游乐场环境。从这里,您决定是否要将某些东西存储在纸上,以便以后存储在石头中。
HEAD是指向当前分支引用的指针,而分支引用又是指向该分支上最后一次提交的指针。
这意味着HEAD将成为下一个提交的父节点。通常,最简单的方式是将HEAD视为该分支上最后一次提交的快照。
简而言之,HEAD始终在不同的提交之间移动,它不能帮助您在暂存步骤或沙盒之间移动。

11
回到我的回答 :) - mfaani
2
精彩的HEAD解释 - MdFarzan

18

Git中的HEAD指针

Git维护了一个称为HEAD的参照变量。我们将这个变量称为指针,因为它的目的是在存储库中引用或指向特定的提交。当我们创建新的提交时,指针会改变或移动以指向新的提交。HEAD始终指向存储库中当前分支的末端。现在,这与我们的暂存区或工作目录无关。

另一种思考方式是仓库的上一个状态或最后检出的内容,因为它是仓库停止的地方,你也可以说HEAD指向下次提交的父提交或者是写入提交的地方。

我认为一个好的比喻来理解这个是磁带录音机上的播放和录音头。当我们开始录制音频时,磁带经过头部并记录到它上面。当我们按下停止键时,记录头所停留的位置是下次按下录制键时开始录制的位置。现在我们可以四处移动,我们可以将头部移动到不同的位置,但是当我们再次按下录制键时,无论头部的位置在哪里,它都将从那里开始录制。

Git中的HEAD指针非常类似,它指向我们下一次要开始记录的位置。它是我们已提交内容在存储库中离开的地方。


1

简单来说,HEAD 是指当前检出分支中的最后一次提交。

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

您可以通过执行以下操作查看 HEAD 指向何处:

cat .git/HEAD

HEAD可以指向一个与分支名称无关的特定版本。这种情况被称为分离的HEAD。


-1

基本上,HEAD是一个指针/引用,它指向当前分支中的最后一次提交。

您可以使用这两个命令来验证此内容。

$ git log -1

commit 9883e13257f2e7555eb6e3b14b2c814978c75692 (HEAD -> MyLocalBranch)
Author: vikram <vikramguptavit@gmail.com>
Date:   Sun Oct 11 23:32:45 2020 -0400
this is my last commit message

现在使用以下命令查看 HEAD 指向的位置:

$ git rev-parse HEAD
9883e13257f2e7555eb6e3b14b2c814978c75692

正如你所看到的,这两个提交哈希相同。因此,HEAD始终指向当前分支中最新/最后一次提交。


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