为什么我使用 Git GUI 结帐时没有列出我的标签?
Why isn't my tag listed when I checkout with Git GUI?
我有一个本地 Git 存储库,其中包含三个带注释的标签:v0.1.0
、v0.1.1
和 v0.1.2
.
当我使用 gitk
查看我的项目历史时(存储库 → 可视化大师的历史),我可以看到分配给正确提交的每个标签。
但是,当我尝试在 Git GUI 中签出我的标签时(分支 → 签出... → 标签),v0.1.1
的标签没有出现。
当我去检查 gitk 中的每个标签时,我注意到 v0.1.0
和 v0.1.2
的详细信息将它们列为 type commit
,而 v0.1.1
的标签被列为 type tag
.
值得注意的是,我重写了此标签的历史记录以修复拼写错误。我使用 git tag <tag name> <tag name> -f -m "<new message>"
.
编辑了我的标签消息
为什么在使用 Git GUI 结帐时看不到我的 v0.1.1
标签?为什么显示为type tag
?
标签可以 point to any object 在 git 存储库中。如果您的标签类型是 "tag",那么您有一个标签指向另一个标签。
轻量级标签不是对象;因此,它们没有自己的哈希 ID,也没有其他任何东西(如另一个标签)可以指向它们。它们实际上只是 easy-to-remember 指向某个对象的哈希 ID 的名称,比分支名称小一点。
然而,带注释的标签是个对象;它们就像提交一样,有自己的消息、作者、创建日期,最重要的是,它们有自己的哈希 ID。这意味着,有些令人困惑的是,它们可以被标记。
果然,正如您在 , this is exactly what happened. Acting on the advice found in How do you rename a Git tag? 中所述,您做了以下操作:
# avoid this...
git tag new old
由于 old
是一个带注释的标签,new
标签的目标将是 old
标签,而不是它指向的提交。
如果你想重命名一个带注释的标签,你应该使用
git tag -a new old^{}
old^{}
将 dereference the tag recursively until a non-tag object is found(在我们的例子中是提交),并将其用作 new
.
的目标对象
为了进一步说明:假设您有一个存储库...哦,就像这个:https://github.com/cyborgx37/sandbox/releases
在此 repo 中,您创建一个带注释的标签,如下所示:
> git tag -m "Version 0.1-beat" v0.1
糟糕...你拼错了 "beta",而且你还决定要将标签名称设为 v0.1-b
。由于这已经发布,您决定执行 sane thing and just create a new tag. Following advice you found on the internet,通过复制第一个标签来创建您真正想要的标签(我附加 __tag
的原因将会变得很清楚):
> git tag -m "Version 0.1-beta" v0.1-b__tag v0.1
只是,这些是带注释的标签,意味着它们是真实的对象。所以当你创建v0.1-b__tag
时,你实际上指向了v0.1
。使用 cat-file
和 show
.
可以清楚地看到结果
这里是v0.1
:
> git cat-file -p v0.1
object 5cf4de319291579d4416da8e0eba8a2973f8b0cf
type commit
tag v0.1
tagger JDB <jd@domain.com> 1521058797 -0400
Version 0.1-beat
> git show v0.1
tag v0.1
Tagger: JDB <jd@domain.com>
Date: Wed Mar 14 16:19:57 2018 -0400
Version 0.1-beat
commit 5cf4de319291579d4416da8e0eba8a2973f8b0cf (HEAD -> master, tag: v0.1-b__tag, tag: v0.1, origin/master)
Author: JDB <jd@domain.com>
Date: Tue Oct 10 12:17:00 2017 -0400
add gitignore
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..42d9955
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+file.txt
请注意 v0.1-b__tag
在其目标类型和历史记录方面都不同:
> git cat-file -p v0.1-b__tag
object 889b82584b2294486f4956dfea17b05e6224fb7f
type tag
tag v0.1-b__tag
tagger JDB <jd@domain.com> 1521059058 -0400
Version 0.1-beta
> git show v0.1-b__tag
tag v0.1-b__tag
Tagger: JDB <jd@domain.com>
Date: Wed Mar 14 16:24:18 2018 -0400
Version 0.1-beta
tag v0.1
Tagger: JDB <jd@domain.com>
Date: Wed Mar 14 16:19:57 2018 -0400
Version 0.1-beat
commit 5cf4de319291579d4416da8e0eba8a2973f8b0cf (HEAD -> master, tag: v0.1-b__tag, tag: v0.1, origin/master)
Author: JDB <jd@domain.com>
Date: Tue Oct 10 12:17:00 2017 -0400
add gitignore
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..42d9955
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+file.txt
显然 Git GUI 对于可以签出的对象类型(提交,而不是标签)相当有选择性,因此它会忽略您指向另一个标签的标签。
如果你使用我上面建议的 git tag -a new old^{}
方法,你可以避免戏剧性的事情,并在第一时间得到你想要的。我将创建一个新标签 v0.1-b__commit
指向 v0.1
的提交,而不是直接指向 v0.1
:
> git tag -m "Version 0.1-beta" v0.1-b__commit v0.1^{}
> git cat-file -p v0.1-b__commit
object 5cf4de319291579d4416da8e0eba8a2973f8b0cf
type commit
tag v0.1-b__commit
tagger JDB <jd@domain.com> 1521059039 -0400
Version 0.1-beta
> git show v0.1-b__commit
tag v0.1-b__commit
Tagger: JDB <jd@domain.com>
Date: Wed Mar 14 16:23:59 2018 -0400
Version 0.1-beta
commit 5cf4de319291579d4416da8e0eba8a2973f8b0cf (HEAD -> master, tag: v0.1-b__tag, tag: v0.1-b__commit, tag: v0.1, origin/master)
Author: JDB <jd@domain.com>
Date: Tue Oct 10 12:17:00 2017 -0400
add gitignore
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..42d9955
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+file.txt
我通常不使用任何 Git 图形用户界面,所以 GUI-specific 部分,我无法真正回答——但你观察到 之间存在差异带注释的标签和轻量级标签是spot-on,是的,How do you rename a Git tag?[的一些答案中应该有一些警告]
When I went to check each tag in gitk, I noticed that the tag details were slightly different. The details for v0.1.0
and v0.1.2
listed them as type commit
, while the tag for v0.1.1
was listed as type tag. I suspect this may be the cause of my problem ...
让我们理清它们之间的区别,并谈谈标签背后的机制。
在Git中,任何实际提交的"true name"是提交的哈希ID。哈希 ID 是又长又丑的 impossible-to-remember 字符串,例如显示在您的一个 GUI 窗格中的 ca5728b6...
。我创建了一个新的空存储库并在其中进行了一次提交:
$ git init
Initialized empty Git repository in ...
$ echo for testing tags > README
$ git add README
$ git commit -m initial
[master (root-commit) a912caa] initial
1 file changed, 1 insertion(+)
create mode 100644 README
$ git rev-parse HEAD
a912caa83de69ef8e5e3e06c3d74b6c409068572
这标识了一个提交,我们可以看到使用 git cat-file -t
,它告诉我们每个内部 Git 对象的 类型 :
$ git cat-file -t a912c
commit
大丑ID可以缩写,只要缩写是独一无二的,至少4个字母即可。1
无论如何,现在让我们制作两个不同的标签,指向同一个提交:
$ git tag -m "an annotated tag" annotag
$ git tag lightweight
并使用 git for-each-ref
检查它们:
$ git for-each-ref
a912caa83de69ef8e5e3e06c3d74b6c409068572 commit refs/heads/master
dc4695ffede0a877fdc61dc06f5ad5c6d5cfc356 tag refs/tags/annotag
a912caa83de69ef8e5e3e06c3d74b6c409068572 commit refs/tags/lightweight
带注释的标签与轻量级标签具有不同的哈希 ID。
这里的技巧是轻量级标签在参考数据库中仅创建一个名称,在本例中为refs/tags/lightweight
。参考数据库中的名称存储哈希 ID,因此这个存储我们单次提交的哈希 ID。
另一方面,带注释的标签作为实际的存储库对象存在,因此我们可以使用 git cat-file
:
检查其类型并查看其内容
$ git cat-file -t dc4695ffede0a877fdc61dc06f5ad5c6d5cfc356
tag
$ git cat-file -p dc4695ffede0a877fdc61dc06f5ad5c6d5cfc356 | sed 's/@/ /'
object a912caa83de69ef8e5e3e06c3d74b6c409068572
type commit
tag annotag
tagger Chris Torek <chris.torek gmail.com> 1521059496 -0700
an annotated tag
请注意,在 存储库 数据库中,由哈希 ID 键入并包含对象数据的带注释的标记对象包含提交的哈希 ID。实际上,还有一个名为 refs/tags/annotag
的 "lightweight-like" 标记指向带注释的标记对象。但是因为它指向一个带注释的标签对象,所以它被视为带注释的标签。
创建新标签时,可以将其指向任何现有对象。让我们看一下与单个提交关联的对象:
$ git cat-file -p HEAD | sed 's/@/ /'
tree 4d73be7092200632865da23347ba0af4ac6c91f7
author Chris Torek <chris.torek gmail.com> 1521053169 -0700
committer Chris Torek <chris.torek gmail.com> 1521053169 -0700
initial
这个提交对象引用了一个树对象,我们可以检查它:
$ git cat-file -p 4d73be7092200632865da23347ba0af4ac6c91f7
100644 blob 938c7cff87a9b753ae70d91412d3ead5c95ef932 README
并且树指向一个 blob 对象,我们也可以检查它:
$ git cat-file -p 938c7cff87a9b753ae70d91412d3ead5c95ef932
for testing tags
这是文件的内容README
。让我们标记一下:
$ git tag the-file 938c7cff87a9b753ae70d91412d3ead5c95ef932
并检查其类型:
$ git cat-file -t the-file
blob
这不是标签的正常使用,但它是允许的。让我们尝试为带注释的标签制作一个轻量级标签:
$ git tag maybe-light annotag
$ git cat-file -t maybe-light
tag
$ git cat-file -p maybe-light | sed 's/@/ /'
object a912caa83de69ef8e5e3e06c3d74b6c409068572
type commit
tag annotag
tagger Chris Torek <chris.torek gmail.com> 1521059496 -0700
an annotated tag
这个maybe-light
标签指向属于注释标签annotag
的注释标签对象。 maybe-light
是 注释标签 吗?这取决于你的观点,不是吗?我会说它既是也不是:它是一个指向带注释的标签的轻量级标签,但它不是 与带注释的标签对象同名的轻量级标签,它声称在对象内部是 / belong-to annotag
。但我还要说,在某种程度上,annotag
既是轻量级标签又是带注释的标签:它是一种轻量级标签,提供带注释的标签对象的 ID。它们使用相同的名称,所以我将其称为 "annotated tag" 并将 refs/tags/annotag
称为标签名称,与 refs/tags/maybe-light
是标签名称的方式相同。
无论如何,我们也可以制作更多的带注释的标签指向这些对象中的任何一个。如果我们使一个带注释的标签指向另一个带注释的标签,我们最终会在存储库中得到两个带注释的标签对象:
$ git tag -m "also annotated" anno2
$ git for-each-ref
a912caa83de69ef8e5e3e06c3d74b6c409068572 commit refs/heads/master
060527046d210f0219170cdc6354afe4834ddc6d tag refs/tags/anno2
dc4695ffede0a877fdc61dc06f5ad5c6d5cfc356 tag refs/tags/annotag
a912caa83de69ef8e5e3e06c3d74b6c409068572 commit refs/tags/lightweight
dc4695ffede0a877fdc61dc06f5ad5c6d5cfc356 tag refs/tags/maybe-light
938c7cff87a9b753ae70d91412d3ead5c95ef932 blob refs/tags/the-file
从这里可以看出anno2
多了一个对象,0605...
:
$ git cat-file -p 0605 | sed 's/@/ /'
object a912caa83de69ef8e5e3e06c3d74b6c409068572
type commit
tag anno2
tagger Chris Torek <chris.torek gmail.com> 1521060518 -0700
also annotated
与此同时,git for-each-ref
将 maybe-light
标签描述为 tag
而不是 commit
:这只是告诉我们它的直接目标对象,而没有遵循到进一步的对象,是一个标签,而不是一个提交。
让我们为 blob 再制作一个带注释的标签:
$ git tag -m "annotated blob" annoblob the-file
因为它是一个带注释的标签,git for-each-ref
说它的类型是 tag
(试一试!)。
Git 调用跟踪标签到其最终对象 "peeling the tag" 的过程,并且有一个特殊的语法:
$ git rev-parse annotag annotag^{} annoblob annoblob^{}
dc4695ffede0a877fdc61dc06f5ad5c6d5cfc356
a912caa83de69ef8e5e3e06c3d74b6c409068572
398b3b89e0377b8942e2f84c97a24afaad0dccb0
938c7cff87a9b753ae70d91412d3ead5c95ef932
请注意,这与仅跟随标记 一次 不同,正如我们看到的,如果我们以这种方式解析 anno2
:
$ git rev-parse anno2^{}
a912caa83de69ef8e5e3e06c3d74b6c409068572
a912...
是提交的ID,不是第二个注释标签。比较:
$ git rev-parse anno2 anno2^{tag}
060527046d210f0219170cdc6354afe4834ddc6d
060527046d210f0219170cdc6354afe4834ddc6d
第一个找到anno2
指向的对象的ID;第二个验证它是类型 tag
的数据库对象。两者当然是同一个ID,确实是tag
类型的对象。我们可以专门要求提交:
$ git rev-parse anno2^{commit}
a912caa83de69ef8e5e3e06c3d74b6c409068572
但如果我们使用名称 annoblob
执行此操作,我们会收到错误消息:
$ git rev-parse annoblob^{commit}
error: annoblob^{commit}: expected commit type, but the object
dereferences to blob type
这就是 ^{}
语法存在的原因:它意味着 跟随标签,直到到达 non-tag,无论它是什么。
1four-character限制意味着如果你命名一个分支cab
,你就可以了。但是,如果您将其命名为 face
,那是 分支名称吗? 还是 原始哈希 ID?如果不止一件事呢?提示见the gitrevisions documentation,但答案是:这取决于命令。如果您拼出引用,refs/heads/face
甚至只是 heads/face
,它不再类似于分支名称 和 缩写的哈希 ID。不幸的是 git checkout
需要朴素的名称 face
(但如果可以的话,总是将其视为分支名称)。
总结
一个标签名只是refs/tags/
name-space中的一个名字。 git tag
命令可以创建新的标签名称。此名称必须指向某个哈希 ID; ID 可以是任何现有对象的 ID,或者您可以 git tag
创建一个新的标签对象。
一个标签对象或带注释的标签对象是存储库数据库中的一个实体。它有一个唯一的哈希 ID,就像提交一样。它的类型为 tag
(相对于提交,其类型为 commit
)。它的元数据由目标对象、标记器名称、标记名称、您喜欢的任何消息和可选的 PGP 签名组成。
标记对象的目标对象 是存储库数据库中的任何现有对象。创建标签对象时该对象需要存在。这可以防止带注释的标签指向自身,或指向您尚未创建的标签对象,从而防止图中出现循环。
运行 git tag
创建一个新标签,要么只创建指向某个现有对象的标签名称,要么创建指向某个现有对象的新标签对象的标签名称。现有对象,无论它是什么,都将继续存在。
运行 git tag -d
仅删除 标签名称 。标签 object,如果有,则保留在存储库中。与提交对象一样,当且仅当没有其他引用可以到达标记对象时,它最终将成为 garbage-collected 并被丢弃。 (这会在将来某个时间发生,当 git gc
运行时。)
我有一个本地 Git 存储库,其中包含三个带注释的标签:v0.1.0
、v0.1.1
和 v0.1.2
.
当我使用 gitk
查看我的项目历史时(存储库 → 可视化大师的历史),我可以看到分配给正确提交的每个标签。
但是,当我尝试在 Git GUI 中签出我的标签时(分支 → 签出... → 标签),v0.1.1
的标签没有出现。
当我去检查 gitk 中的每个标签时,我注意到 v0.1.0
和 v0.1.2
的详细信息将它们列为 type commit
,而 v0.1.1
的标签被列为 type tag
.
值得注意的是,我重写了此标签的历史记录以修复拼写错误。我使用 git tag <tag name> <tag name> -f -m "<new message>"
.
为什么在使用 Git GUI 结帐时看不到我的 v0.1.1
标签?为什么显示为type tag
?
标签可以 point to any object 在 git 存储库中。如果您的标签类型是 "tag",那么您有一个标签指向另一个标签。
轻量级标签不是对象;因此,它们没有自己的哈希 ID,也没有其他任何东西(如另一个标签)可以指向它们。它们实际上只是 easy-to-remember 指向某个对象的哈希 ID 的名称,比分支名称小一点。
然而,带注释的标签是个对象;它们就像提交一样,有自己的消息、作者、创建日期,最重要的是,它们有自己的哈希 ID。这意味着,有些令人困惑的是,它们可以被标记。
果然,正如您在
# avoid this...
git tag new old
由于 old
是一个带注释的标签,new
标签的目标将是 old
标签,而不是它指向的提交。
如果你想重命名一个带注释的标签,你应该使用
git tag -a new old^{}
old^{}
将 dereference the tag recursively until a non-tag object is found(在我们的例子中是提交),并将其用作 new
.
为了进一步说明:假设您有一个存储库...哦,就像这个:https://github.com/cyborgx37/sandbox/releases
在此 repo 中,您创建一个带注释的标签,如下所示:
> git tag -m "Version 0.1-beat" v0.1
糟糕...你拼错了 "beta",而且你还决定要将标签名称设为 v0.1-b
。由于这已经发布,您决定执行 sane thing and just create a new tag. Following advice you found on the internet,通过复制第一个标签来创建您真正想要的标签(我附加 __tag
的原因将会变得很清楚):
> git tag -m "Version 0.1-beta" v0.1-b__tag v0.1
只是,这些是带注释的标签,意味着它们是真实的对象。所以当你创建v0.1-b__tag
时,你实际上指向了v0.1
。使用 cat-file
和 show
.
这里是v0.1
:
> git cat-file -p v0.1
object 5cf4de319291579d4416da8e0eba8a2973f8b0cf
type commit
tag v0.1
tagger JDB <jd@domain.com> 1521058797 -0400
Version 0.1-beat
> git show v0.1
tag v0.1
Tagger: JDB <jd@domain.com>
Date: Wed Mar 14 16:19:57 2018 -0400
Version 0.1-beat
commit 5cf4de319291579d4416da8e0eba8a2973f8b0cf (HEAD -> master, tag: v0.1-b__tag, tag: v0.1, origin/master)
Author: JDB <jd@domain.com>
Date: Tue Oct 10 12:17:00 2017 -0400
add gitignore
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..42d9955
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+file.txt
请注意 v0.1-b__tag
在其目标类型和历史记录方面都不同:
> git cat-file -p v0.1-b__tag
object 889b82584b2294486f4956dfea17b05e6224fb7f
type tag
tag v0.1-b__tag
tagger JDB <jd@domain.com> 1521059058 -0400
Version 0.1-beta
> git show v0.1-b__tag
tag v0.1-b__tag
Tagger: JDB <jd@domain.com>
Date: Wed Mar 14 16:24:18 2018 -0400
Version 0.1-beta
tag v0.1
Tagger: JDB <jd@domain.com>
Date: Wed Mar 14 16:19:57 2018 -0400
Version 0.1-beat
commit 5cf4de319291579d4416da8e0eba8a2973f8b0cf (HEAD -> master, tag: v0.1-b__tag, tag: v0.1, origin/master)
Author: JDB <jd@domain.com>
Date: Tue Oct 10 12:17:00 2017 -0400
add gitignore
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..42d9955
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+file.txt
显然 Git GUI 对于可以签出的对象类型(提交,而不是标签)相当有选择性,因此它会忽略您指向另一个标签的标签。
如果你使用我上面建议的 git tag -a new old^{}
方法,你可以避免戏剧性的事情,并在第一时间得到你想要的。我将创建一个新标签 v0.1-b__commit
指向 v0.1
的提交,而不是直接指向 v0.1
:
> git tag -m "Version 0.1-beta" v0.1-b__commit v0.1^{}
> git cat-file -p v0.1-b__commit
object 5cf4de319291579d4416da8e0eba8a2973f8b0cf
type commit
tag v0.1-b__commit
tagger JDB <jd@domain.com> 1521059039 -0400
Version 0.1-beta
> git show v0.1-b__commit
tag v0.1-b__commit
Tagger: JDB <jd@domain.com>
Date: Wed Mar 14 16:23:59 2018 -0400
Version 0.1-beta
commit 5cf4de319291579d4416da8e0eba8a2973f8b0cf (HEAD -> master, tag: v0.1-b__tag, tag: v0.1-b__commit, tag: v0.1, origin/master)
Author: JDB <jd@domain.com>
Date: Tue Oct 10 12:17:00 2017 -0400
add gitignore
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..42d9955
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+file.txt
我通常不使用任何 Git 图形用户界面,所以 GUI-specific 部分,我无法真正回答——但你观察到 之间存在差异带注释的标签和轻量级标签是spot-on,是的,How do you rename a Git tag?[的一些答案中应该有一些警告]
When I went to check each tag in gitk, I noticed that the tag details were slightly different. The details for
v0.1.0
andv0.1.2
listed them astype commit
, while the tag forv0.1.1
was listed as type tag. I suspect this may be the cause of my problem ...
让我们理清它们之间的区别,并谈谈标签背后的机制。
在Git中,任何实际提交的"true name"是提交的哈希ID。哈希 ID 是又长又丑的 impossible-to-remember 字符串,例如显示在您的一个 GUI 窗格中的 ca5728b6...
。我创建了一个新的空存储库并在其中进行了一次提交:
$ git init
Initialized empty Git repository in ...
$ echo for testing tags > README
$ git add README
$ git commit -m initial
[master (root-commit) a912caa] initial
1 file changed, 1 insertion(+)
create mode 100644 README
$ git rev-parse HEAD
a912caa83de69ef8e5e3e06c3d74b6c409068572
这标识了一个提交,我们可以看到使用 git cat-file -t
,它告诉我们每个内部 Git 对象的 类型 :
$ git cat-file -t a912c
commit
大丑ID可以缩写,只要缩写是独一无二的,至少4个字母即可。1
无论如何,现在让我们制作两个不同的标签,指向同一个提交:
$ git tag -m "an annotated tag" annotag
$ git tag lightweight
并使用 git for-each-ref
检查它们:
$ git for-each-ref
a912caa83de69ef8e5e3e06c3d74b6c409068572 commit refs/heads/master
dc4695ffede0a877fdc61dc06f5ad5c6d5cfc356 tag refs/tags/annotag
a912caa83de69ef8e5e3e06c3d74b6c409068572 commit refs/tags/lightweight
带注释的标签与轻量级标签具有不同的哈希 ID。
这里的技巧是轻量级标签在参考数据库中仅创建一个名称,在本例中为refs/tags/lightweight
。参考数据库中的名称存储哈希 ID,因此这个存储我们单次提交的哈希 ID。
另一方面,带注释的标签作为实际的存储库对象存在,因此我们可以使用 git cat-file
:
$ git cat-file -t dc4695ffede0a877fdc61dc06f5ad5c6d5cfc356
tag
$ git cat-file -p dc4695ffede0a877fdc61dc06f5ad5c6d5cfc356 | sed 's/@/ /'
object a912caa83de69ef8e5e3e06c3d74b6c409068572
type commit
tag annotag
tagger Chris Torek <chris.torek gmail.com> 1521059496 -0700
an annotated tag
请注意,在 存储库 数据库中,由哈希 ID 键入并包含对象数据的带注释的标记对象包含提交的哈希 ID。实际上,还有一个名为 refs/tags/annotag
的 "lightweight-like" 标记指向带注释的标记对象。但是因为它指向一个带注释的标签对象,所以它被视为带注释的标签。
创建新标签时,可以将其指向任何现有对象。让我们看一下与单个提交关联的对象:
$ git cat-file -p HEAD | sed 's/@/ /'
tree 4d73be7092200632865da23347ba0af4ac6c91f7
author Chris Torek <chris.torek gmail.com> 1521053169 -0700
committer Chris Torek <chris.torek gmail.com> 1521053169 -0700
initial
这个提交对象引用了一个树对象,我们可以检查它:
$ git cat-file -p 4d73be7092200632865da23347ba0af4ac6c91f7
100644 blob 938c7cff87a9b753ae70d91412d3ead5c95ef932 README
并且树指向一个 blob 对象,我们也可以检查它:
$ git cat-file -p 938c7cff87a9b753ae70d91412d3ead5c95ef932
for testing tags
这是文件的内容README
。让我们标记一下:
$ git tag the-file 938c7cff87a9b753ae70d91412d3ead5c95ef932
并检查其类型:
$ git cat-file -t the-file
blob
这不是标签的正常使用,但它是允许的。让我们尝试为带注释的标签制作一个轻量级标签:
$ git tag maybe-light annotag
$ git cat-file -t maybe-light
tag
$ git cat-file -p maybe-light | sed 's/@/ /'
object a912caa83de69ef8e5e3e06c3d74b6c409068572
type commit
tag annotag
tagger Chris Torek <chris.torek gmail.com> 1521059496 -0700
an annotated tag
这个maybe-light
标签指向属于注释标签annotag
的注释标签对象。 maybe-light
是 注释标签 吗?这取决于你的观点,不是吗?我会说它既是也不是:它是一个指向带注释的标签的轻量级标签,但它不是 与带注释的标签对象同名的轻量级标签,它声称在对象内部是 / belong-to annotag
。但我还要说,在某种程度上,annotag
既是轻量级标签又是带注释的标签:它是一种轻量级标签,提供带注释的标签对象的 ID。它们使用相同的名称,所以我将其称为 "annotated tag" 并将 refs/tags/annotag
称为标签名称,与 refs/tags/maybe-light
是标签名称的方式相同。
无论如何,我们也可以制作更多的带注释的标签指向这些对象中的任何一个。如果我们使一个带注释的标签指向另一个带注释的标签,我们最终会在存储库中得到两个带注释的标签对象:
$ git tag -m "also annotated" anno2
$ git for-each-ref
a912caa83de69ef8e5e3e06c3d74b6c409068572 commit refs/heads/master
060527046d210f0219170cdc6354afe4834ddc6d tag refs/tags/anno2
dc4695ffede0a877fdc61dc06f5ad5c6d5cfc356 tag refs/tags/annotag
a912caa83de69ef8e5e3e06c3d74b6c409068572 commit refs/tags/lightweight
dc4695ffede0a877fdc61dc06f5ad5c6d5cfc356 tag refs/tags/maybe-light
938c7cff87a9b753ae70d91412d3ead5c95ef932 blob refs/tags/the-file
从这里可以看出anno2
多了一个对象,0605...
:
$ git cat-file -p 0605 | sed 's/@/ /'
object a912caa83de69ef8e5e3e06c3d74b6c409068572
type commit
tag anno2
tagger Chris Torek <chris.torek gmail.com> 1521060518 -0700
also annotated
与此同时,git for-each-ref
将 maybe-light
标签描述为 tag
而不是 commit
:这只是告诉我们它的直接目标对象,而没有遵循到进一步的对象,是一个标签,而不是一个提交。
让我们为 blob 再制作一个带注释的标签:
$ git tag -m "annotated blob" annoblob the-file
因为它是一个带注释的标签,git for-each-ref
说它的类型是 tag
(试一试!)。
Git 调用跟踪标签到其最终对象 "peeling the tag" 的过程,并且有一个特殊的语法:
$ git rev-parse annotag annotag^{} annoblob annoblob^{}
dc4695ffede0a877fdc61dc06f5ad5c6d5cfc356
a912caa83de69ef8e5e3e06c3d74b6c409068572
398b3b89e0377b8942e2f84c97a24afaad0dccb0
938c7cff87a9b753ae70d91412d3ead5c95ef932
请注意,这与仅跟随标记 一次 不同,正如我们看到的,如果我们以这种方式解析 anno2
:
$ git rev-parse anno2^{}
a912caa83de69ef8e5e3e06c3d74b6c409068572
a912...
是提交的ID,不是第二个注释标签。比较:
$ git rev-parse anno2 anno2^{tag}
060527046d210f0219170cdc6354afe4834ddc6d
060527046d210f0219170cdc6354afe4834ddc6d
第一个找到anno2
指向的对象的ID;第二个验证它是类型 tag
的数据库对象。两者当然是同一个ID,确实是tag
类型的对象。我们可以专门要求提交:
$ git rev-parse anno2^{commit}
a912caa83de69ef8e5e3e06c3d74b6c409068572
但如果我们使用名称 annoblob
执行此操作,我们会收到错误消息:
$ git rev-parse annoblob^{commit}
error: annoblob^{commit}: expected commit type, but the object
dereferences to blob type
这就是 ^{}
语法存在的原因:它意味着 跟随标签,直到到达 non-tag,无论它是什么。
1four-character限制意味着如果你命名一个分支cab
,你就可以了。但是,如果您将其命名为 face
,那是 分支名称吗? 还是 原始哈希 ID?如果不止一件事呢?提示见the gitrevisions documentation,但答案是:这取决于命令。如果您拼出引用,refs/heads/face
甚至只是 heads/face
,它不再类似于分支名称 和 缩写的哈希 ID。不幸的是 git checkout
需要朴素的名称 face
(但如果可以的话,总是将其视为分支名称)。
总结
一个标签名只是refs/tags/
name-space中的一个名字。 git tag
命令可以创建新的标签名称。此名称必须指向某个哈希 ID; ID 可以是任何现有对象的 ID,或者您可以 git tag
创建一个新的标签对象。
一个标签对象或带注释的标签对象是存储库数据库中的一个实体。它有一个唯一的哈希 ID,就像提交一样。它的类型为 tag
(相对于提交,其类型为 commit
)。它的元数据由目标对象、标记器名称、标记名称、您喜欢的任何消息和可选的 PGP 签名组成。
标记对象的目标对象 是存储库数据库中的任何现有对象。创建标签对象时该对象需要存在。这可以防止带注释的标签指向自身,或指向您尚未创建的标签对象,从而防止图中出现循环。
运行 git tag
创建一个新标签,要么只创建指向某个现有对象的标签名称,要么创建指向某个现有对象的新标签对象的标签名称。现有对象,无论它是什么,都将继续存在。
运行 git tag -d
仅删除 标签名称 。标签 object,如果有,则保留在存储库中。与提交对象一样,当且仅当没有其他引用可以到达标记对象时,它最终将成为 garbage-collected 并被丢弃。 (这会在将来某个时间发生,当 git gc
运行时。)