我正在浏览一些 git 内部,遇到了标签以及它们在内部的存储方式。在 Git SCM 站点中,我发现了以下内容:
签出标签
您无法真正签出 Git 中的标签,因为它们无法移动 周围。如果要将存储库的一个版本放入您的 看起来像特定标签的工作目录,您可以创建一个新的 使用 git checkout -b [分支名称] [标记名称] 的特定标记处分支:
git checkout -b version2 v2.0.0
切换到新的分支"版本 2">
当然,如果您这样做并执行提交,则您的版本2分支将是 与您的 v2.0.0 标签略有不同,因为它会向前移动 对于您的新更改,请务必小心。
我有以下疑问:
- 为什么我们不能签出标签?我们不能遍历 Git TAG 来
HEAD
指向标签指向的提交吗? - 我们是否可以创建一个新的提交,然后指向提交 由标签指向父级?
- 据说如果我们从标签创建一个新分支并提交,它会略有不同。他们在谈论什么区别?新提交(假设没有任何变化)不会将标记的提交称为其父提交,而其他所有内容(树和斑点)保持不变吗?
谢谢。
编辑:
我指的是HEAD和标签指向不同提交的情况。
为什么我们不能签出标签?
我们可以!
我们不能遍历 Git TAG 以使 HEAD 指向标签指向的提交吗?
是的,我们可以做到这一点(尽管严格来说,我们甚至不需要太多(如果有的话):提交是快照,所以一旦我们到达正确的快照,我们就完成了,标签通常需要零个或一个中间步骤才能到达提交)。
HEAD
文件(.git
名为HEAD
的实际文件)更典型的状态是它包含分支的名称,而不是提交的 ID。 这种模式没有太多正式的名称,但我称之为"在分支上",以与其他模式形成对比。
当.git/HEAD
直接指向某个提交(通过包含其哈希 ID)时,我们得到的模式,正如 CodeWizard 在注释中提到的,称为"分离的 HEAD"。 由于.git/HEAD
不再包含分支的名称,我们现在没有分支——或者,等效地,在某种未命名的分支上,我们可以在其中使用@
符号(从 Git 版本 1.8.5 开始)来命名它,或者拼写出单词HEAD
。 当然,HEAD
是一个好名字;例如,Git 会在我们再次运行git checkout master
时立即覆盖.git/HEAD
文件的内容,然后我们将不再将哈希 ID 存储在任何地方。
我们可以创建一个新的提交,然后将指向标签指向的提交作为父级吗?
英语在这里有点模棱两可,但我认为图表更清楚地表明答案是"是":
initially: on branch `master` with commit `H` as the current commit
...--F--G <-- tag: v2.0.0
H <-- master (HEAD)
在这里,将H
点提交回去提交G
;提交G
点以提交F
;依此类推。 名为master
的分支指向提交H
,标记名称v2.0.0
指向提交G
(如果是轻量级标记,则直接提交,或者通过一个带注释的标记对象,如果是带注释的标记)。
After `git checkout v2.0.0`:
...--F--G <-- HEAD, tag: v2.0.0
H <-- master
.git/HEAD
文件现在包含提交G
的哈希 ID
If we make a new commit now:
I <-- HEAD
/
...--F--G <-- tag: v2.0.0
H <-- master
正如您所建议的,新提交I
将提交G
作为其父级。.git/HEAD
文件现在包含提交I
的哈希 ID
据说如果我们从标签创建一个新分支并提交,它会略有不同。他们在谈论什么区别?新提交(假设没有任何变化)不会将标记的提交称为其父提交,而其他所有内容(树和斑点)保持不变吗?
提交内容将是相同的,但提交图的绘制会略有变化。 这是新的图形绘图,从相同的起点开始,但使用git checkout -b version2 v2.0.0
作为中间步骤。
initially: on branch `master` with commit `H` as the current commit
...--F--G <-- tag: v2.0.0
H <-- master (HEAD)
After `git checkout -b version2 v2.0.0`:
...--F--G <-- version2 (HEAD), tag: v2.0.0
H <-- master
If we make a new commit now:
I <-- version2 (HEAD)
/
...--F--G <-- tag: v2.0.0
H <-- master
请注意,在本例中(我们"在名为version2
的分支上"),当我们进行新提交时,分支名称会随之移动。 当我们不在任何分支上时,在"分离的 HEAD"模式下,新提交只是更新(存储在.git/HEAD
中的提交哈希 ID)。 但是,当.git/HEAD
包含分支的名称时,这些新提交会更新该分支。.git/HEAD
文件本身只是继续包含分支的名称。
事实上,这就是"在分支上"的含义:每当.git/HEAD
包含分支的名称时,各种操作(特别是包括进行新提交和使用git reset
)都会更改分支名称记住的哈希 ID,而不是更改.git/HEAD
本身。 每当.git/HEAD
包含提交的哈希 ID时,这些相同的操作只会.git/HEAD
自身更新。
git checkout
命令通常更新.git/HEAD
的内容,是少数可以更改存储在.git/HEAD
中的分支名称的命令之一。 (我在这里说"通常"git checkout
因为它有点像厨房水槽命令,具有各种模式来做奇怪的事情,因为它们与它通常做的主要事情密切相关。 具体来说,如果你在一个分支上,git checkout
可以将你放在另一个分支上;如果你不在任何分支上,git checkout
可以让你进入一个分支;如果您使用--detach
或标签名称或类似名称,git checkout
可以将您置于分离的 HEAD 模式;如果您不在任何分支上,git checkout
可以更改HEAD
指向哪个提交。
(git reset
命令可以执行其中的一些操作,但它不会带您进入或离开任何分支。 像git checkout
一样,它有点"厨房下沉":它可以做很多事情,如果没有很多文字和几个图表,很难很好地描述这些!