Image of What is an Annotated Tag in Git?

ADVERTISEMENT

Table of Contents

Introduction

Git users who are aware of some of Git's internals understand that Git has 3 core object types which are stored in the object database - blobs, trees, and commits.

In this article, we'll discuss Git's 4th object, the annotated tag.

What are Git tags?

A Git tag is type of ref (like a branch name), which is essentially just a label that points to a specific commit. Tags are created using the git tag command.

Git has 2 main types of tags - lightweight tags and annotated tags. When you create either type of tag, a ref for that tag is created and stored in the .git/refs/tags folder. The ref is just a file with the tag's name that contains the commit ID that the tag is attached to.

What is the difference between Git lightweight tags and Git annotated tags?

If both lightweight and annotated tags create a ref in the .git/refs/tags folder containing the commit ID that the tag points to, what's the difference between them?

Well, the difference is that's all there is for lightweight Git tags. On the other hand, annotated tags store an additional message - or annotation - in addition to the actual tag name.

Since annotated tags actually store user-generated content besides the tag name, Git actually formats it into an object, hashes it, and stores it in the object database.

Create a lightweight tag in Git

You're probably familiar with creating lightweight tags in Git. Lightweight tags are the default type of tag created when you run git tag like:

$ git tag v1.0

As mentioned, this just creates a new tag ref file here: .git/refs/tags/v1.0.

Feel free to test this out and open the newly created ref file v1.0 to see that it just contains the commmit ID that it refers to.

Create an annotated tag in Git

Annotated tags are created by simply adding the -a flag to the git tag command:

$ git tag v2.0 -a

This will name the tag v2.0 (just like a lightweight tag), but in addition Git will open your default text editor for you to enter the annotation message, similar to how it works for commit messages without the -m flag. So for this tag you can enter something like This is version 2.0.

If you want to specify the annotated tag message directly on the command line, you can use the -m flag exactly like you do for a commit message:

$ git tag v3.0 -m "This is version 3.0"

Note that using the -m flag implies that this will be an annotated tag, so you don't need to include the -a flag in this case.

List annotated tag message

You can list an annotated tag message by using the git tag command along with the tags name and the -n flag:

$ git tag v3.0 -n
v3.0            This is version 3.0

Annotated tag in object database

As previously mentioned, since annotated tags contain a custom message that can't simply be stored as a ref filename, Git will actually create a new object for each tag in the Git repository.

This means that each annotated tag content is hashed, compressed, and stored in the object database like any other Git object. To get the SHA-1 hash value of the annotated tag itself, you can use the git rev-parse command along with the tag name:

$ git rev-parse v3.0
bd0bf094154490236f7fb7d4d48c105151631fa1

We can verify that this is a tag object using the git cat-file command along with the -t flag:

$ git cat-file -t bd0bf094154490236f7fb7d4d48c105151631fa1
tag

Furthermore, we can see the actual tag's storage format and content using git cat-file again but with the -p flag:

$ git cat-file -p bd0bf094154490236f7fb7d4d48c105151631fa1
object 051ef0038ca3d8273bb6750ccf07c624821f9c4d
type commit
tag v3.0
tagger Jacob Stopak <jacob@initialcommit.io> 1664374520 -0700

This is version 3.0

This shows that annotated tags store the following information:

  1. The object ID of the commit that the tag points to (in this case 051ef0038ca3d8273bb6750ccf07c624821f9c4d)
  2. The type of object that the tag points to, in this case commit
  3. The tag name, in this case v3.0
  4. The tagger - i.e. the user who created the tag and tag time
  5. The annotation message itself, in this case This is version 3.0

Summary

In this article, we provided a detailed overview of annotated tags in Git. We explained that tags are just labels associated with specific commits. We also saw that annotated tags differ from lightweight tags by accepted a customized message that is stored as a full object in the repository.

We learned how to created annotated tags and list them along with their messages. Finally, we popped the hood and saw the actual format and structure of an annotated tag as stored in Git's repository.

Next Steps

If you're interested in learning more about how Git works under the hood, check out our Baby Git Guidebook for Developers, which dives into Git's code in an accessible way. We wrote it for curious developers to learn how Git works at the code level. To do this, we documented the first version of Git's code and discuss it in detail.

We hope you enjoyed this post! Feel free to shoot me an email at jacob@initialcommit.io with any questions or comments.

Final Notes