I usually donโt use any of the Git GUIs, so I canโt answer the details depending on the GUI, but your observation that the difference between annotated tags and light tags is blurring, and yes, there should be some warning in some answers to How do you rename a Git tag?
When I went to check every tag in gitk, I noticed that the details of the tag are slightly different. Details for v0.1.0 and v0.1.2 designated them as type commit , and the tag for v0.1.1 was specified as a type tag. I suspect this may be causing my problem ...
Let me clarify the difference between the two and talk about tag mechanisms.
In Git, the "true name" of any actual commit is the hash identifier of the commit. Hash identifiers are long, ugly, impossible to remember strings, such as ca5728b6... , displayed in one of your GUI panels. I created a new, empty repository and made one commit in it:
$ 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
This identifies the commit, and we can see that with git cat-file -t , which tells us the type of each internal Git object:
$ git cat-file -t a912c commit
You can shorten large ugly identifiers as long as the abbreviation is unique and has at least four letters. one
In any case, now let's make two different tags, pointing to the same commit:
$ git tag -m "an annotated tag" annotag $ git tag lightweight
and use git for-each-ref to check them:
$ git for-each-ref a912caa83de69ef8e5e3e06c3d74b6c409068572 commit refs/heads/master dc4695ffede0a877fdc61dc06f5ad5c6d5cfc356 tag refs/tags/annotag a912caa83de69ef8e5e3e06c3d74b6c409068572 commit refs/tags/lightweight
An annotated tag has a different hash identifier than a light tag.
The trick here is that the light tag only creates the name in the reference database, in this case refs/tags/lightweight . The names are in the identifiers of the hash records of the control database, so this one stores the hash identifier of our single commit.
An annotated tag, on the other hand, exists as an object of a real repository, so we can check its type and see its contents using 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
Please note that the annotated tag object in the repository database with the hash identifier key contains the object data and contains the commit hash identifier. In fact, there is also a "light weight" tag with the name refs/tags/annotag pointing to the object of the annotated tag. But since it points to an annotated tag object, it is treated as an annotated tag.
When you create a new tag, you can point it to any existing object. Let's take a look at the objects associated with single commit:
$ 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
This commit object refers to a tree object that we can verify:
$ git cat-file -p 4d73be7092200632865da23347ba0af4ac6c91f7 100644 blob 938c7cff87a9b753ae70d91412d3ead5c95ef932 README
and the tree points to a blob object, which we can also check:
$ git cat-file -p 938c7cff87a9b753ae70d91412d3ead5c95ef932 for testing tags
which is the contents of the README . Let the tag:
$ git tag the-file 938c7cff87a9b753ae70d91412d3ead5c95ef932
and check its type:
$ git cat-file -t the-file blob
This is not the usual use of a tag, but it is allowed. Try making an easy tag for an annotated tag:
$ 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
This maybe-light tag points to an annotated tag object that belongs to the annotag annotated tag. Is maybe-light an annotated tag? It depends on your point of view, right? I would say that this is and is not: it is a light tag pointing to an annotated tag, but it is not a light tag that has the same name as the object of the annotated tag, which claims directly inside the object that will / belongs to - to annotag . But I would also say that in a sense, annotag is both a lightweight and an annotated tag: it is a lightweight tag that gives the object identifier of the annotated tag. They use the same name, so I would call it an โannotated tagโ and designate refs/tags/annotag as the tag name, just as refs/tags/maybe-light is the tag name.
In any case, we can also make more annotated tags that point to any of these objects. If we make an annotated tag that points to another annotated tag, we end up with two annotated tag objects:
$ 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
You can see from this that anno2 has a new object, 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
Meanwhile, git for-each-ref describes the maybe-light tag as a tag , not a commit : it just tells us that its immediate target, not following other objects, is a tag, not a commit.
Make another annotated tag, for blob:
$ git tag -m "annotated blob" annoblob the-file
Since this is an annotated tag, git for-each-ref says its type is tag (try it!).
Git calls the tag tracking process to its final tag peeling object, and there is a special syntax for it:
$ git rev-parse annotag annotag^{} annoblob annoblob^{} dc4695ffede0a877fdc61dc06f5ad5c6d5cfc356 a912caa83de69ef8e5e3e06c3d74b6c409068572 398b3b89e0377b8942e2f84c97a24afaad0dccb0 938c7cff87a9b753ae70d91412d3ead5c95ef932
Note that this is different from the following immediately after the tag, as we can see that we are analyzing anno2 as follows:
$ git rev-parse anno2^{} a912caa83de69ef8e5e3e06c3d74b6c409068572
a912... is the commit id, not the second annotated tag. Compare with:
$ git rev-parse anno2 anno2^{tag} 060527046d210f0219170cdc6354afe4834ddc6d 060527046d210f0219170cdc6354afe4834ddc6d
The first finds the identifier of the object to which anno2 points; the second checks that it is a database object of type tag . Both of them, of course, are the same ID, and this is really an object of type tag . We can specifically ask about fixing:
$ git rev-parse anno2^{commit} a912caa83de69ef8e5e3e06c3d74b6c409068572
but if we do this with the name annoblob , we get an error:
$ git rev-parse annoblob^{commit} error: annoblob^{commit}: expected commit type, but the object dereferences to blob type
therefore, the ^{} syntax exists: it means the following tags until you reach a tag, whatever that is.
1 The four-character limit means that if you name the branch cab , you are fine. If you name it face , is that the name of the branch or the original hash identifier? What if it can be several? See the gitrevisions documentation for tips, but the answer is this: it depends on the command. If you specified a link, refs/heads/face or even just heads/face , it no longer resembles a branch name and an abbreviated hash id. Unfortunately, git checkout requires the unvarnished name of face (but always treats it as a branch name if it can).
Summary
A tag name is simply a name in the refs/tags/ namespace. The git tag command can create new tag names. This name must point to some hash identifier; the identifier can be the identifier of any existing object, or you can git tag create a new tag object.
A tag object or an annotated tag object is an object in the repository database. It has a unique hash identifier, just like commit. It has a tag type (vs commit, which has a commit type). Its metadata consists of a target, a tagger name, a tag name, any message you like, and an optional PGP signature.
A tag object's target is any existing object in the repository database. This object must exist when creating the tag object. This prevents the annotated tag from pointing to itself or a tag object that you have not yet created, which prevents cycles in the chart.
Running git tag to create a new tag creates either just a tag name that points to some existing object, or a tag name that points to a new tag object that points to some existing object. An existing object, no matter what it is, continues to exist.
Running git tag -d removes only the tag name. The tag object, if any, remains in the repository. Like commit objects, it will ultimately be collected and disposed of in the garbage if and only if there are no other links that can be used to reach the tag object. (This happens in the future when git gc is executed.)