---
title: Tagging
source: 'https://academia.sh/en/courses/introduction-to-version-control/tagging'
course: 'Introduction to Version Control'
language: en
updated: '2026-08-17T18:10:48+00:00'
license: 'CC BY-SA 4.0'
---

# Tagging

Giving permanent names to specific points in history; the difference between a lightweight tag and a tag object, the fourth object type, locating position by tag, and semantic versioning.

Up to this point, points in history have been referred to by forty-character IDs or by
relative notation like `HEAD~1`. Neither is a permanent name: an ID cannot be kept in
mind, and relative notation points at a different commit as history grows.

A **tag** is a fixed name given to a specific commit. Its difference from branch tips is
that it does not budge when a new commit is written: the branch advances, the tag stays.

## Lightweight Tag

The plainest form binds a commit ID to a name:

```bash
git tag v1.0.0 498105a
git tag -l
```

```
v1.0.0
```

```bash
cat .git/refs/tags/v1.0.0
```

```
498105a461806443b43bf3287f41a9fe045f7ef8
```

The file's content is directly the commit ID. A **lightweight tag** creates no new
object in the object database; it only writes a ref file.

```bash
git cat-file -t v1.0.0
```

```
commit
```

The name points at a commit. Who placed the tag, when, and why is recorded nowhere.
This is insufficient for marking a published release.

```bash
git tag -d v1.0.0
```

```
Deleted tag 'v1.0.0' (was 498105a)
```

## The Tag Object

```bash
git tag -a v1.0.0 -m "İlk kararlı sürüm: arama, harf duyarsızlığı, hata kodu" 498105a
git cat-file -t v1.0.0
```

```
tag
```

The object type changed. An **annotated tag** is the last of the four object types
introduced in the second topic, and it is written to the object database:

```bash
git cat-file -p v1.0.0
```

```
object 498105a461806443b43bf3287f41a9fe045f7ef8
type commit
tag v1.0.0
tagger Deniz Kaya <deniz@ornek.test> 1710309600 +0300

İlk kararlı sürüm: arama, harf duyarsızlığı, hata kodu
```

There are five components: the ID and type of the pointed-at object, the tag name, the
person who placed the tag together with the time, and the description text. The
structure resembles a commit object — and it is named by the same rule:

```bash
git rev-parse v1.0.0
```

```
8fa8557633aff60f8bc5bce08050ea0c524d866f
```

This ID belongs to the tag object, not the commit. To reach the pointed-at commit, a
type conversion is requested:

```bash
git rev-parse "v1.0.0^{commit}"
```

```
498105a461806443b43bf3287f41a9fe045f7ef8
```

Most commands perform this conversion on their own; the distinction shows up only when
the object itself is asked for directly.

```bash
git show v1.0.0 --stat
```

```
tag v1.0.0
Tagger: Deniz Kaya <deniz@ornek.test>
Date:   Wed Mar 13 09:00:00 2024 +0300

İlk kararlı sürüm: arama, harf duyarsızlığı, hata kodu

commit 498105a461806443b43bf3287f41a9fe045f7ef8
Author: Deniz Kaya <deniz@ornek.test>
Date:   Tue Mar 12 09:48:00 2024 +0300

    Aramanın harf duyarsızlığını README'de belirt

 README.md | 2 ++
 1 file changed, 2 insertions(+)
```

The output shows the tag first, then the commit it points at.

| Criterion | Lightweight tag | Annotated tag |
|---|---|---|
| Creates an object | No | Yes |
| Records who and when | No | Yes |
| Description text | No | Yes |
| Use | Temporary, local marker | Published release |

## Listing Tags

After the listing option is finished, a second release is tagged:

```bash
git tag -a v1.1.0 -m "Listeleme seçeneği (-l) eklendi"
git tag -l
```

```
v1.0.0
v1.1.0
```

```bash
git tag -l "v1.0*"
```

```
v1.0.0
```

```bash
git tag -n
```

```
v1.0.0          İlk kararlı sürüm: arama, harf duyarsızlığı, hata kodu
v1.1.0          Listeleme seçeneği (-l) eklendi
```

Tags also show up in the history list:

```bash
git log --oneline --decorate -n 5
```

```
396729b (HEAD -> main, tag: v1.1.0) README'de -l seçeneğini belgele
701fa37 Tüm terimleri listeleyen -l seçeneğini ekle
498105a (tag: v1.0.0) Aramanın harf duyarsızlığını README'de belirt
a58791f Revert "Arama çıktısını yalnızca Türkçe sütuna indir"
ad60f31 Arama çıktısını yalnızca Türkçe sütuna indir
```

All the refs can also be listed with a single command:

```bash
git for-each-ref refs/tags
```

```
8fa8557633aff60f8bc5bce08050ea0c524d866f tag	refs/tags/v1.0.0
2054d80edbfbbb29b1da99ba01f3e121b7b50a39 tag	refs/tags/v1.1.0
```

The second column gives the object type; for lightweight tags, this column would read
`commit`.

## Locating Position by Tag

```bash
git describe
```

```
v1.1.0
```

When a new commit is written after the tag, the output changes:

```bash
git describe
```

```
v1.1.0-1-g2aff8c5
```

There are three parts: the nearest tag, the number of commits written since that tag,
and the current commit's shortened ID. The `g` at the start of the ID marks the value as
a git object. When this string is embedded in an executable program's version
information, it says exactly which commit the binary in hand was built from.

## Semantic Versioning

A tag's name is free; but there is a common scheme for marking releases: **semantic
versioning**. A version number consists of three numbers separated by dots, and
incrementing each number makes a promise:

| Part | When it increments | What it tells the user |
|---|---|---|
| Major | When backward compatibility breaks | Adaptation may be needed |
| Minor | When a compatible new capability is added | Existing use is not broken |
| Patch | When only a fix is made | Behavior is the same, a flaw was fixed |

In the example repository, `v1.0.0` marked the first stable release, and `v1.1.0`
marked the addition of a backward-compatible capability: the `-l` option does not break
previous use, it only adds a new form of invocation. If the script's exit codes had been
changed, the major version would have needed to increment.

The scheme's value lies less in the number itself than in the promise it makes. The
concept of backward compatibility was defined in the Programming Fundamentals course, in
the context of language choice; a version number is a machine-readable declaration of
that same concept.

## The Limits of Tags

Tags can be moved, but they should not be. If a tag is shifted to another commit after
being published, copies that already have that tag keep pointing at the old commit; the
same name ends up naming two different pieces of content. The point a published tag
points at is not changed; a new tag is placed instead, if one is needed.

Deletion carries the same problem. A tag deleted locally keeps standing in other copies
of the repository.

Tags can also point at objects other than commits — a tag can be placed on a blob or a
tree. In practice this is rarely used; a tag's usual target is a commit.

## Summary

- A tag is a fixed name given to a commit, one that does not get displaced by new
  commits.
- A lightweight tag is only a ref file; an annotated tag is a separate object storing
  who placed it and a description.
- The tag object is the fourth object type, and it is named by the same rule as the
  others.
- `git describe` gives the number of commits since the nearest tag and the current ID.
- Semantic versioning promises backward compatibility with a three-number version.
- Published tags are not moved or deleted; a new tag is placed when needed.

## Course Wrap-Up

A single repository was built across the course, and it reached its present state
through eighteen commits:

```bash
git log --oneline --decorate
```

```
2aff8c5 (HEAD -> main) Bileşik terim örneği ekle
396729b (tag: v1.1.0) README'de -l seçeneğini belgele
701fa37 Tüm terimleri listeleyen -l seçeneğini ekle
498105a (tag: v1.0.0) Aramanın harf duyarsızlığını README'de belirt
a58791f Revert "Arama çıktısını yalnızca Türkçe sütuna indir"
ad60f31 Arama çıktısını yalnızca Türkçe sütuna indir
18600c5 README'ye kullanım bölümü ekle
bfe57d3 Öbek ve dizi terimlerini ekle
cb8f7ee Yerel notları izlemeden çıkar
7783378 Yerel notları ekle
9b23cc3 Üretilen dosyaları yoksayma listesine al
ece664b Küme terimini ekle
2c883a7 Eşleşme bulunamadığında hata bildir
60e13bd Aramayı büyük/küçük harften bağımsız yap
43370ca Üç veri yapısı terimi ekle
3132c78 Biçim örneğindeki yazımı düzelt
546c174 Terim arama betiği ekle
13d31bf Terim listesini ve biçim belgesini ekle
```

This list shows the course's structure: files being added, atomic commits, a mistake
being committed and reverted, ignore rules being set up, and two releases being tagged.

The course's real subject was not the commands but the model underneath them. Four
object types — blob, tree, commit, tag — are named by content-addressed storage;
commits build a directed acyclic graph through their parent links; the staging area
accumulates the next snapshot to be added to that graph. Every command is a consequence
of these three sentences. Why amending produces a new ID, why reverting does not disturb
history, why the same content gives the same ID — the answer to all three is in the
model.

One subject was deliberately left out across the course: history stayed a single chain
throughout. Yet the graph's definition allowed branching, and `--graph`'s output coming
out as a single column was not a limitation — it was a property of this particular
repository.

The next course — **Branching and Collaboration** — multiplies that column. It takes up
the fact that a branch is nothing but a pointer, how two branches are merged, how
conflicts are resolved, and how history is synced with remote repositories. The model
built in this course holds there too: a branch is nothing but a file under `refs/heads`;
a merge is nothing but a commit with two parents.
