I have a local Git repository with three annotated tags: v0.1.0
, v0.1.1
, and v0.1.2
.
When I view my project's history with gitk
(Repository → Visualize master's history), I can see each tag assigned to the proper commit.
However, when I try to checkout my tags in Git GUI (Branch → Checkout... → Tags), the tag for v0.1.1
doesn't appear.
When I went to check each tag in gitk, I noticed that 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
.
It's worth noting that I've rewritten history on this tag to fix a typo. I edited my tag message using git tag <tag name> <tag name> -f -m "<new message>"
.
Why can't I see my v0.1.1
tag when checking out with Git GUI? Why does it appear as type tag
?
Tags can point to any object in the git repository. If your tag type is "tag", then you have a tag pointing to another tag.
Lightweight tags are not objects; thus, they have no hash ID of their own and nothing else (like another tag) can point to them. They are literally just easy-to-remember names pointing to some object's hash ID, a little less than a branch name.
However, annotated tags are objects; they are like commits, with their own message, author, created date and, most importantly, their own hash ID. This means that, somewhat confusingly, they can be tagged.
Sure enough, as you described in your comment, this is exactly what happened. Acting on the advice found in How do you rename a Git tag?, you did the following:
# avoid this...
git tag new old
Since old
was an annotated tag, the target for the new
tag will be the old
tag, not the commit that it was pointing to.
If you want to rename an annotated tag, you should use
git tag -a new old^{}
old^{}
will dereference the tag recursively until a non-tag object is found (in our case, a commit), and use that as the target object for new
.
To further illustrate: let's say you have a repo... oh, like this one: https://github.com/cyborgx37/sandbox/releases
In this repo you create an annotated tag like so:
> git tag -m "Version 0.1-beat" v0.1
Oh shoot... you misspelled "beta" and also you've decided that you want the tag name to be v0.1-b
. Since this has already been published, you decide to do the sane thing and just create a new tag. Following advice you found on the internet, you create the tag you actually wanted (I appended __tag
for reasons that will become clear) by copying the first tag:
> git tag -m "Version 0.1-beta" v0.1-b__tag v0.1
Only, these are annotated tags, meaning they are actual objects. So when you created v0.1-b__tag
, you actually pointed it at v0.1
. You can see the result clearly using cat-file
and show
.
Here's v0.1
:
> git cat-file -p v0.1
object 5cf4de319291579d4416da8e0eba8a2973f8b0cf
type commit # ⇦ v0.1 is a tag which points to a commit
tag v0.1
tagger JDB <jd@domain.com> 1521058797 -0400
Version 0.1-beat
> git show v0.1
# v0.1 is a tag
# ⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩
tag v0.1
Tagger: JDB <jd@domain.com>
Date: Wed Mar 14 16:19:57 2018 -0400
Version 0.1-beat
# which is pointing directly to a commit
# ⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩
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
Notice that v0.1-b__tag
is different both in its target type as well as its history:
> git cat-file -p v0.1-b__tag
object 889b82584b2294486f4956dfea17b05e6224fb7f
type tag # ⇦ v0.1-b__tag is a tag which points to a tag
tag v0.1-b__tag
tagger JDB <jd@domain.com> 1521059058 -0400
Version 0.1-beta
> git show v0.1-b__tag
# v0.1-b__tag is a tag
# ⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩
tag v0.1-b__tag
Tagger: JDB <jd@domain.com>
Date: Wed Mar 14 16:24:18 2018 -0400
Version 0.1-beta
# which is pointing to the v0.1 tag
# ⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩
tag v0.1
Tagger: JDB <jd@domain.com>
Date: Wed Mar 14 16:19:57 2018 -0400
Version 0.1-beat
# which is pointing to the intended target commit
# ⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩
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
Apparently Git GUI is rather selective about what types of objects can be checked out (commits, not tags), so it's ignoring your tag pointing at another tag.
If you use the git tag -a new old^{}
approach I suggested above, you can avoid the drama and get what you wanted in the first place. I'll create a new tag, v0.1-b__commit
that points to v0.1
's commit, rather than to v0.1
directly:
> 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