查看 Git 上的标签

Checking out tags on Git

我正在研究一些 git 内部结构,发现了标签及其内部存储方式。在 Git SCM site 中,我发现了以下内容:

Checking out Tags

You can’t really checkout a tag in Git, since they can’t be moved around. If you want to put a version of your repository in your working directory that looks like a specific tag, you can create a new branch at a specific tag with git checkout -b [branchname] [tagname]:

git checkout -b version2 v2.0.0

Switched to a new branch 'version2'

Of course if you do this and do a commit, your version2 branch will be slightly different than your v2.0.0 tag since it will move forward with your new changes, so do be careful.

我有以下疑惑:

  1. 为什么我们不能签出标签?难道我们不能遍历Git TAG,让HEAD指向tag指向的commit吗?
  2. 我们可以创建一个新的提交然后指向该提交吗 被标记指向父级?
  3. 据说如果我们从标签创建一个新分支并提交,它会略有不同。他们在说什么区别?新提交(假设没有任何变化)不会将标记的提交作为其父项,而其他所有内容(树和斑点)保持不变吗?

谢谢。

编辑:
我指的是 HEAD 和标签指向不同提交的情况。

Why can't we check out tags?

我们可以!

Can't we traverse the Git TAG to make HEAD point to the commit pointed to by the tag?

是的,我们可以做到这一点(虽然,严格来说,我们甚至不需要太多遍历:提交是快照,所以一旦我们到达正确的,我们就完成了,标签通常需要到达提交的零个或一个中间步骤)。

HEAD 文件(它是 .git 中名为 HEAD 的实际文件)的更典型状态是它包含分支的 名称,而不是提交的 ID。这种模式没有太多的正式名称,但我称它为"being on a branch",以与其他模式进行对比。

.git/HEAD 直接指向某个提交(通过包含其哈希 ID)时我们得到的模式是 ,称为 "detached HEAD"。由于 .git/HEAD 不再包含分支的名称,我们现在在 no 分支上——或者,等价地,在某种未命名的分支上,我们可以在其中使用 @ 符号(自 Git 版本 1.8.5)来命名它,或者通过拼写单词 HEAD。当然 HEAD 是个好名字;只是 Git 会在我们再次 运行 git checkout master 时覆盖 .git/HEAD 文件的内容,例如,然后我们将不再存储哈希 ID任何地方。

Can we create a new commit which will then point to the commit pointed to by tag as the parent?

英文这里有点模棱两可,不过我觉得一张图更清楚答案是"yes":

 initially: on branch `master` with commit `H` as the current commit

 ...--F--G     <-- tag: v2.0.0
          \
           H   <-- master (HEAD)

这里commitH指向commitG; commit G 指向commit F;等等。名为 master 的分支指向提交 H,标签名称 v2.0.0 指向提交 G(如果是轻量级标签,则直接指向,或者通过一个 带注释的标签对象,如果是带注释的标签)。

 After `git checkout v2.0.0`:

 ...--F--G     <-- HEAD, tag: v2.0.0
          \
           H   <-- master

.git/HEAD 文件现在包含提交的哈希 ID G

If we make a new commit now:

           I   <-- HEAD
          /
 ...--F--G     <-- tag: v2.0.0
          \
           H   <-- master

新提交 I 已按照您的建议将提交 G 作为其父项。 .git/HEAD 文件现在包含提交的哈希 ID I

It is said that if we create a new branch from the tag and commit, it'll be slightly different. What difference are they talking about? Won't the new commit (assuming nothing has changed) refer to the tagged commit as its parent with everything else (trees & blobs) remaining the same?

提交内容将相同,但提交图的绘制会稍有不同。这是新的图形绘制,从相同的起点开始,但使用 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

请注意,在这种情况下——我们 "on a branch" 命名为 version2 的那个——分支名称随着我们进行新提交而移动。当我们不在任何分支上时,在 "detached HEAD" 模式下,新提交只是更新(存储在 .git/HEAD 中的提交哈希 ID)。但是当 .git/HEAD 包含 分支名称 时,那些新提交 更新该分支 .git/HEAD 文件本身只是继续包含分支的名称。


事实上,这就是 "on a branch" 的含义:每当 .git/HEAD 包含 分支名称 时,各种操作——具体包括进行新提交并使用 git reset——更改分支名称记住的 哈希 ID,而不是更改 .git/HEAD 本身。每当 .git/HEAD 包含提交的 哈希 ID 时,这些相同的操作只会更新 .git/HEAD 本身。

git checkout命令通常更新.git/HEAD的内容,是为数不多的可以改变存储在分支名称的命令之一17=]。 (我在这里说 "usually" 是因为 git checkout 有点像 kitchen-sink command,有各种模式可以做奇怪的事情,因为它们与它通常做的主要事情密切相关。)具体来说,如果你在一个分支上,git checkout可以把你放在一个不同的分支上;如果您不在任何分支机构,git checkout 可以让您进入一个分支机构;如果您使用 --detach 或标签名称或类似名称,git checkout 可以让您进入分离 HEAD 模式;如果你不在任何分支上,git checkout 可以更改 HEAD 指向的提交。

(git reset命令可以做其中的一些事情,但它不会带你进出任何分支。像git checkout,有点"kitchen sinky":它它可以做很多事情,没有很多文字和图表很难描述这些!)