Tags in Git can be likened to checkpoints of a game. You can tag points in a repository’s history that are important to you. Typically, tags are used to mark release versions like v1.0.0, v2.0.0, etc.

Tags are associated with a specific commit in your repository. In other words, after you create a commit, you can tag that commit with any name of you liking - e.g., v1.0.0, v2.0.0, etc.

In this post, I am going to tell you about the two types of tags in Git and how to create them.

Types of tags

There are two types of tags in Git:

  1. Lightweight
  2. Annotated

Lightweight tags

These tags are simply pointers to a commit. You can think of them as “bookmarks” to a commit. They cannot store any metadata apart from the name of the tag itself. As a best practice, these tags are generally used for temporary bookmarking and private use.

The command to create a lightweight tag is:

git tag <tagname>

where tagname can be anything that you want.

Annotated tags

Annotated tags are stored as independent objects in the Git database, with its own SHA1 hash, and metadata. They are capable of storing metadata such as author of the tag, email of the tagger, date of tagging and a message describing the tag. The command to create an annotated tag is:

git tag -a <tagname> -m <message>

# Example
git tag -a v1.0.0 -m "App is open to the public now"

If you provide a message using the -m flag, then you don’t even need to provide the -a flag. You can just do: git tag <tagname> -m <message>

Annotated tags are useful when you want to push the tag to the public domain. They are useful because they can hold metadata like tagger’s name and date of tagging. This information can help, for example, when trying to track the root cause of a bug.

Can a tag only point to the latest commit?

No. You can also create a tag that points to a previous commit. Consider the following git log:

305e248 - Ayush Poddar, 10 weeks ago : refactor: split into two methods
6f7f4ab - Ayush Poddar, 10 weeks ago : modify the instance doubles ensuring green specs
fe59bb7 - Ayush Poddar, 10 weeks ago : add logic to show different color for hidden files/directories
4f1333f - Ayush Poddar, 10 weeks ago : add colors for hidden files/directories
1e1f10c - Ayush Poddar, 10 weeks ago : modify indentations
28c188b - Claudio Bley, 3 months ago : Merge pull request #594 from ayushpoddar/show-abs-path-xargs
44c370b - Ayush Poddar, 3 months ago : Revert "version bump"

What if we want to create a tag that points to commit 1e1f10c (modify indentations)? You can simply add the commit hash (1e1f10c) at the end of the git tag command. So the command would look like this:

# Lightweight tag
git tag <tagname> 1e1f10c

# Annotated tag
git tag -a <tagname> -m <tag message> 1e1f10c

Viewing details of a tag

To view details of a tag, you can use the git show command. The command is:

git show <tagname>

The output differs slightly for lightweight and annotated tags. In case of lightweight tags, only the associated commit’s information is displayed. The sample output is shown below.

commit 305e2486f4431fc7e0487423f90ed380852194b2
Author: Ayush Poddar <[email protected]>
Date:   Wed Jun 7 18:00:34 2023 +0530

    refactor: split into two methods

diff --git a/lib/colorls/core.rb b/lib/colorls/core.rb
index b558968..f5f7707 100644
--- a/lib/colorls/core.rb
+++ b/lib/colorls/core.rb
@@ -416,20 +416,32 @@ module ColorLS
 
     def options(content)
       if content.directory?
-        key = content.name.downcase.to_sym
-        key = @folder_aliases[key] unless @folders.key? key
-        key = :folder if key.nil?
-        color = content.hidden? ? @colors[:hidden_dir] : @colors[:dir]
-        group = :folders
+        options_directory(content).values_at(:key, :color, :group)
       else
-        key = File.extname(content.name).delete_prefix('.').downcase.to_sym
-        key = @file_aliases[key] unless @files.key? key
-        color = file_color(content, key)
-        group = @files.key?(key) ? :recognized_files : :unrecognized_files
-        key = :file if key.nil?
+        options_file(content).values_at(:key, :color, :group)
       end
+    end
+
+    def options_directory(content)
+      key = content.name.downcase.to_sym
+      key = @folder_aliases[key] unless @folders.key?(key)
+      key = :folder if key.nil?
+
+      color = content.hidden? ? @colors[:hidden_dir] : @colors[:dir]
+
+      {key: key, color: color, group: :folders}
+    end
+
+    def options_file(content)
+      key = File.extname(content.name).delete_prefix('.').downcase.to_sym
+      key = @file_aliases[key] unless @files.key?(key)
+
+      color = file_color(content, key)
+      group = @files.key?(key) ? :recognized_files : :unrecognized_files
+
+      key = :file if key.nil?
 
-      [key, color, group]
+      {key: key, color: color, group: group}
     end
 
     def tree_contents(path)

In case of annotated tags, additional metadata such as tagger name, tagging date, etc. are also displayed. A sample output is given below. Notice the metadata displayed above the commit information.

tag annotated-tag
Tagger: Ayush Poddar <[email protected]>
Date:   Tue Aug 15 13:38:36 2023 +0530

This is the sample tag

commit 305e2486f4431fc7e0487423f90ed380852194b2
Author: Ayush Poddar <[email protected]>
Date:   Wed Jun 7 18:00:34 2023 +0530

    refactor: split into two methods

diff --git a/lib/colorls/core.rb b/lib/colorls/core.rb
index b558968..f5f7707 100644
--- a/lib/colorls/core.rb
+++ b/lib/colorls/core.rb
@@ -416,20 +416,32 @@ module ColorLS
 
     def options(content)
       if content.directory?
-        key = content.name.downcase.to_sym
-        key = @folder_aliases[key] unless @folders.key? key
-        key = :folder if key.nil?
-        color = content.hidden? ? @colors[:hidden_dir] : @colors[:dir]
-        group = :folders
+        options_directory(content).values_at(:key, :color, :group)
       else
-        key = File.extname(content.name).delete_prefix('.').downcase.to_sym
-        key = @file_aliases[key] unless @files.key? key
-        color = file_color(content, key)
-        group = @files.key?(key) ? :recognized_files : :unrecognized_files
-        key = :file if key.nil?
+        options_file(content).values_at(:key, :color, :group)
       end
+    end
+
+    def options_directory(content)
+      key = content.name.downcase.to_sym
+      key = @folder_aliases[key] unless @folders.key?(key)
+      key = :folder if key.nil?
+
+      color = content.hidden? ? @colors[:hidden_dir] : @colors[:dir]
+
+      {key: key, color: color, group: :folders}
+    end
+
+    def options_file(content)
+      key = File.extname(content.name).delete_prefix('.').downcase.to_sym
+      key = @file_aliases[key] unless @files.key?(key)
+
+      color = file_color(content, key)
+      group = @files.key?(key) ? :recognized_files : :unrecognized_files
+
+      key = :file if key.nil?
 
-      [key, color, group]
+      {key: key, color: color, group: group}
     end
 
     def tree_contents(path)

Listing tags

To list all tags in a repository, you can use the git tag command (without any arguments). You can also supply the --list (-l) flag which takes an optional argument.

Using git tag --list, as it is, will also list all the tags just like git tag would. The sample output of running git tag --list or git tag is shown below.

v1.0.0
v1.0.1
v1.0.2
v1.0.3
v1.0.4
v1.0.5
v1.0.7
v1.0.8
v1.0.9
v1.1.0
v1.1.1
v1.2.0
v1.3.0
v1.3.1
v1.3.2
v1.3.3
v1.4.0
v1.4.1
v1.4.2
v1.4.3
v1.4.4
v1.4.5
v1.4.6

How to filter the tags by their name?

You can provide an argument to the --list flag to filter the tags. The argument needs to be the pattern that you want to filter by. The pattern can be the exact string to match or a shell wildcard. For example, if we want to view only those tags which start with “v1.0”, we can do:

$ git tag --list 'v1.0.*'

# Output
v1.0.0
v1.0.1
v1.0.2
v1.0.3
v1.0.4
v1.0.5
v1.0.7
v1.0.8
v1.0.9

How to filter for multiple patterns?

The --list flag can also take multiple arguments. In such a case, all tags which match any of the provided patterns will be listed. For example, if we want to list all tags which start with “v1.0”, along with the tags which start with “v1.4” we can do:

$ git tag --list 'v1.0.*' 'v1.4.*'

# Output
v1.0.0
v1.0.1
v1.0.2
v1.0.3
v1.0.4
v1.0.5
v1.0.7
v1.0.8
v1.0.9
v1.4.0
v1.4.1
v1.4.2
v1.4.3
v1.4.4
v1.4.5
v1.4.6

Conclusion

In this post, you learned about Git tags and the different types of tags supported by it. You also learned:

  • how to create tags
  • view details of a specific tag
  • List all the tags and filter them by their name

References