当我使用Git GUI结帐时,为什么我的标签没有列出?

时间:2018-03-14 17:05:14

标签: git git-tag git-gui

我有一个本地Git存储库,其中包含三个带注释的标记:v0.1.2gitkv0.1.1

当我使用v0.1.0 Repository→Visualize master&#39; s history )查看我的项目历史记录时,我可以看到每个标记分配给正确的提交。< / p>

Project history in gitk

但是,当我尝试在Git GUI( Branch→Checkout ...→Tags )中签出我的标签时,v0.1.2的标签不会出现。

Git GUI tag list

当我去检查gitk中的每个标签时,我注意到标签的细节略有不同。 type commitv0.1.1的详细信息将其列为type tag,而v0.1.1的标记则列为type tag

Details for tag v0.1.1

值得注意的是,我已经在此标签上重写了历史记录。为了在我的代码说明中修正拼写错误,我使用git tag <tag name> <tag name> -f -m "<new message>"编辑了我的代码消息。

为什么我在使用Git GUI结账时看不到我的<ul>标签?为什么它显示为id

2 个答案:

答案 0 :(得分:10)

git存储库中的标签可以point to any object。如果你的标签类型是“tag”,那么你有一个标签指向另一个标签。

轻量级代码不是对象;因此,他们没有自己的哈希ID,没有别的东西(比如另一个标签)可以指向他们。它们实际上只是易于记忆的名称,指向某个对象的哈希ID,比分支名称少一点。

但是,带注释的标签对象;它们就像提交一样,具有自己的消息,作者,创建日期,最重要的是,它们自己的哈希ID。这意味着,有些令人困惑,它们可以被标记。

果然,正如你在your comment中所描述的那样,这正是发生的事情。根据{{​​3}}中的建议,您执行了以下操作:

# avoid this...
git tag new old

由于old是带注释的标记,new标记的目标将是old标记,而不是它指向的提交。

如果要重命名带注释的标签,则应使用

git tag -a new old^{}

old^{}How do you rename a Git tag?(在我们的示例中为提交),并将其用作new的目标对象。

进一步说明:假设你有一个回购......哦,就像这个:dereference the tag recursively until a non-tag object is found

在这个仓库中,您可以创建一个带注释的标签:

> git tag -m "Version 0.1-beat" v0.1

哦拍摄......你拼错了“beta”,你也决定要让标签名称为v0.1-b。由于这已经发布,您决定执行https://github.com/cyborgx37/sandbox/releases并只创建一个新标记。在sane thing之后,您可以通过复制第一个标记来创建您真正想要的标记(我附加了__tag,原因很明显):

> git tag -m "Version 0.1-beta" v0.1-b__tag v0.1

只有这些是带注释的标签,这意味着它们是实际的对象。因此,当您创建v0.1-b__tag时,您实际上将其指向v0.1。您可以使用cat-fileshow清楚地看到结果。

这是 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

答案 1 :(得分:3)

我通常不会使用任何Git GUI,因此特定于GUI的部分,我无法回答 - 但您观察到带注释的标签之间存在差异< / em>和轻量级标签是正确的,是的,在How do you rename a Git tag?的某些答案中应该有一些警告

  

当我去检查gitk中的每个标签时,我注意到标签的细节略有不同。 v0.1.0v0.1.2的详细信息将其列为type commit,而v0.1.1的标记则列为类型标记。我怀疑这可能是我问题的原因......

让我们弄清楚这些之间的区别,并谈谈标签背后的机制。

在Git,&#34;真名&#34;任何实际提交都是提交的哈希ID。哈希ID是冗长,丑陋,不可记住的字符串,例如在您的一个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。 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。实际上,还有一种轻量级的&#34;名为refs/tags/annotag的标记,指向带注释的标记对象。但由于它指向带注释的标记对象,因此它被视为带注释的标记。

制作新标签时,可以将其指向任何现有对象。让我们来看看与单个提交相关联的对象:

$ 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带注释的标记吗?这取决于你的观点,不是吗?我会说,它既不是也不是:它是一个指向带注释标签的轻量级标签,但它不是 轻量级标签,它的名称相同作为带注释的标记对象,它在对象内部声称是/属于annotag。但我还要说,annotag在某种程度上是一个轻量级和带注释的标签:它是一个轻量级标签,它提供带注释标签对象的ID。他们使用相同的名称,因此我将其称为&#34;带注释的标签&#34;并引用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-refmaybe-light标记描述为tag而不是commit:它只是告诉我们它的直接目标对象,而没有跟进其他对象,是一个标签,而不是一个提交。

让我们为blob制作一个带注释的标签:

$ git tag -m "annotated blob" annoblob the-file

由于它是带注释的标记,git for-each-ref表示其类型为tag(试一试!)。

Git调用跟踪标记到其最终对象的过程&#34;剥离标记&#34;,并且有一个特殊的语法:

$ 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

这就是^{}语法存在的原因:它意味着跟踪标记,直到您到达非标记,无论是什么。

1 四个字符的限制意味着如果您命名一个分支cab,那么您就可以了。但是,如果将其命名为face,那么分支名称还是原始哈希ID ?如果它不止一件怎么办?有关提示,请参阅the gitrevisions documentation,但答案是:它取决于命令。如果您拼写出引用refs/heads/face或甚至只是heads/face,则它不再类似于分支名称​​和缩写的哈希ID。不幸的是,git checkout要求使用简单的名称face(但如果可以,则始终将其视为分支名称。)

摘要

标记名称只是refs/tags/名称空间中的名称。 git tag命令可以创建新的标记名称。此名称必须指向一些哈希ID; ID可以是任何现有对象的ID,也可以让git tag创建新的标记对象。

标记对象带注释的标记对象是存储库数据库中的实体。它具有唯一的哈希ID,就像提交一样。它具有类型tag(与提交相对应,类型为commit)。其元数据由目标对象,标记器名称,标记名称,您喜欢的任何消息以及可选的PGP签名组成。

标记对象的目标对象是存储库数据库中的任何现有对象。创建标记对象时,该对象需要存在。这可以防止带注释的标记指向自身,或者指向尚未创建的标记对象,从而阻止图形中的循环。

运行git tag创建新标记只会创建指向某个现有对象的标记名称,或者创建指向指向某个现有对象的新标记对象的标记名称。现有的对象,无论它是什么,都会继续存在。

正在运行git tag -d只会删除标签名称。标记 object (如果有)保留在存储库中。与提交对象一样,当且仅当没有其他引用可以到达标记对象时,它最终将被垃圾收集并被丢弃。 (当git gc运行时,这将在未来的某个时间发生。)