git中的head到底是怎么工作的,十分钟就够了
背景
一般上讲当说某个话题令人困惑而我认为它并不令人困惑时,原因是其中实际上存在一些我没有考虑到的隐藏复杂性。我发现这HEAD 实际上比我想象的要复杂一些!
HEAD 实际上是一些不同的东西
在与许多不同的人谈论之后HEAD,我意识到 HEAD实际上有几个不同的密切相关的含义:
1)文件 .git/HEAD
2)HEAD就像这样git show HEAD(git 称之为“修订参数”)
3)git 在各种命令HEAD的输出中使用的所有方法( <<<<<<<<<<HEAD、、、、等)(HEAD -> main)detached HEAD stateOn branch main
4)它们彼此之间极其密切相关,但我认为对于刚开始使用 git 的人来说,这种关系并不完全明显。
文件 .git/HEAD
Git 有一个非常重要的文件,叫做.git/HEAD。此文件的工作方式是,它包含以下内容之一:
1)分支的名称(如ref: refs/heads/main)
2)提交ID(例如96fa6899ea34697257e84865fefc56beb42d6390)
此文件决定了 Git 中的“当前分支”。例如,当你运行git status并看到以下内容时:
$ git status
On branch main
这意味着该文件
.git/HEAD
包含
ref: refs/heads/main
。
如果
.git/HEAD
包含的是提交 ID 而不是分支,git 会将其称为“分离的 HEAD 状态”。我们稍后会讲到这一点。
(有时会说 HEAD 包含引用 的名称或提交 ID,但我很确定该引用必须是一个分支。从技术上讲,可以
.git/HEAD
通过手动编辑来包含非分支的引用的名称
.git/HEAD
,但我不认为可以使用常规 git 命令来做到这一点。我很想知道是否有一种常规 git 命令方法可以使
.git/HEAD
成为非分支引用,如果是的话,为什么要这样做!)
HEAD如同git show HEAD
HEAD在 git 命令中引用提交 ID是很常见的,例如:
git diff HEAD
git rebase -i HEAD^^^^
git diff main..HEAD
git reset --hard HEAD@{2}
所有这些(HEAD、HEAD^^^、HEAD@{2})都称为“修订参数”。它们记录在man gitrevisions中,Git 会尝试将它们解析为提交 ID。
(老实说,我以前从未听说过“修订参数”这个术语,但这个术语可以让你了解这个概念的文档)
HEAD 的
git show HEAD
含义非常简单:它解析已检出的当前提交HEAD!Git以两种方式之一解析:
- 如果.git/HEAD包含分支名称,它将是该分支上的最新提交(例如通过从中读取.git/refs/heads/main) 2)如果.git/HEAD包含提交 ID,则为该提交 ID
下一步:所有输出格式
现在我们已经讨论了文件
.git/HEAD
和“修订参数” HEAD。我们剩下的就是 git在其输出中git show HEAD使用的各种方式。HEAD
git status:“在主分支上”或“HEAD 分离”
当运行时git status,第一行将始终看起来像以下两行之一:
on branch main
。这意味着
.git/HEAD
包含一个分支。
HEAD detached at 90c81c72
。这意味着.git/HEAD包含一个提交 ID。现在我来解释“HEAD detached”是什么意思。
分离 HEAD 状态
“HEAD 已分离”或“HEAD 分离状态”意味着您没有当前分支。
没有当前分支有点危险,因为如果你做了新的提交,这些提交将不会附加到任何分支 - 它们将被孤立!孤立提交是个问题,原因有 2 个:
1)提交更难找到(你不能跑去
git log somebranch
寻找它们)
2)孤立的提交最终将被 git 的垃圾收集删除
就我个人而言,我非常小心地避免在分离的 HEAD 状态下创建提交,尽管有些人更喜欢这样工作。但是,退出分离的 HEAD 状态非常容易,您可以:
1)返回分支 ( git checkout main)
2)在该提交处创建一个新分支(git checkout -b newbranch)
3)如果你处于分离 HEAD 状态,因为你正处于变基过程中,请完成或中止变基(git rebase --abort)
好的,回到其他 git 命令的HEAD输出!
git log:(HEAD -> main)
当您运行git log并查看第一行时,可能会看到以下 3 种情况之一:
commit 96fa6899ea (HEAD -> main)
commit 96fa6899ea (HEAD, main)
commit 96fa6899ea (HEAD)
目前还不太清楚如何解释这些,所以情况是这样的:
在 中(…),git 列出了指向该提交的每个引用,例如(HEAD -> main, origin/main, origin/HEAD)表示HEAD、main、origin/main和origin/HEAD都指向该提交(直接或间接)
HEAD -> main意味着你当前的分支是main
如果该行显示的是HEAD,而不是HEAD ->,则表示您处于分离的 HEAD 状态(您没有当前分支)
如果我们用这些规则来解释上面的3个例子:结果是:
1)commit 96fa6899ea (HEAD -> main)方法:
1-1).git/HEAD包含ref: refs/heads/main
1-2).git/refs/heads/main包含96fa6899ea
2)commit 96fa6899ea (HEAD, main)方法:
2-1).git/HEAD包含96fa6899ea(HEAD 处于“分离”状态)
2-2).git/refs/heads/main还包含96fa6899ea
3)commit 96fa6899ea (HEAD)方法:
3-1).git/HEAD包含96fa6899ea(HEAD 处于“分离”状态)
3-2).git/refs/heads/main包含不同的提交 ID 或不存在
合并冲突:<<<<<<< HEAD很令人困惑
当您解决合并冲突时,您可能会看到类似以下内容:
<<<<<<< HEAD
def parse(input):
return input.split("\n")
=======
def parse(text):
return text.split("\n\n")
>>>>>>> somebranch
我发现HEAD这种情况非常令人困惑,我基本上只是忽略它。原因如下:
- 当你进行合并时,合并冲突与运行时的HEAD冲突相同。很简单。
HEAD git merge
2)当你执行rebase时,HEAD合并冲突是完全不同的:这是你正在 rebase 的另一个提交HEAD。所以它与你运行 时的情况完全不同
git rebase
。之所以如此,是因为 rebase 的工作方式是先检出另一个提交,然后反复在其上挑选提交。
类似地,“我们的”和“他们的”的含义在合并和变基时被颠倒了。
事实上,HEAD根据我是否进行变基或合并而改变的含义对我来说确实太令人困惑了,我发现完全忽略HEAD并使用另一种方法来找出代码的哪部分是哪部分要简单得多。
关于术语一致性的一些想法
我认为,如果 git 围绕 HEAD 的术语在内部更加一致,那么 HEAD 会更加直观。
例如,git 会谈论“分离的 HEAD 状态”,但从不谈论“连接的 HEAD 状态”——git 的文档从来没有使用术语“连接”来指代HEAD。git 会谈论“在”分支上,但从不谈论“不在”分支上。
因此很难猜测
on branch main
实际上是的反义词
HEAD detached
。用户如何猜测这HEAD detached和分支有什么关系,或者和“在主分支上”有什么关系HEAD?
就这样!
如果我想到HEAD在 Git 中使用其他方法(尤其是 HEAD 出现在 Git 输出中的方式)会继续补充进来,如果你觉得 HEAD 令人困惑,我希望这篇文章有所帮助!
版权归原作者 神技圈子 所有, 如有侵权,请联系我们删除。