Skip to content
academia.sh

Lesson 15 / 15

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.

Published Updated

In this lesson

  • Distinguishes a lightweight tag from an annotated tag and chooses the appropriate one
  • Reads the content of a tag object
  • Determines version numbers using semantic versioning rules
Contents

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:

git tag v1.0.0 HEAD~1
git tag -l
v1.0.0
cat .git/refs/tags/v1.0.0
11fe7e8840e866dda55da81f522f27f3268f1751

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.

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.

git tag -d v1.0.0
Deleted tag 'v1.0.0' (was 11fe7e8)

The Tag Object

export GIT_COMMITTER_DATE="2024-03-13 09:00:00 +0300"
git tag -a v1.0.0 -m "İlk kararlı sürüm: arama, harf duyarsızlığı, hata kodu" HEAD~1
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:

git cat-file -p v1.0.0
object 11fe7e8840e866dda55da81f522f27f3268f1751
type commit
tag v1.0.0
tagger Deniz Kaya <[email protected]> 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:

git rev-parse v1.0.0
9c57fc4649ef6bdcd4ee73dc697fe500468d50f6

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

git rev-parse "v1.0.0^{commit}"
11fe7e8840e866dda55da81f522f27f3268f1751

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

git show v1.0.0 --stat
tag v1.0.0
Tagger: Deniz Kaya <[email protected]>
Date:   Wed Mar 13 09:00:00 2024 +0300

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

commit 11fe7e8840e866dda55da81f522f27f3268f1751
Author: Deniz Kaya <[email protected]>
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. First the option is documented in the README and committed:

cat >> README.md <<'END'

Tüm terimleri listelemek için `./ara.sh -l` yazın.
END
export GIT_AUTHOR_DATE="2024-03-13 11:00:00 +0300"
export GIT_COMMITTER_DATE="2024-03-13 11:00:00 +0300"
git commit -a -m "README'de -l seçeneğini belgele"

Then the tag is placed:

export GIT_COMMITTER_DATE="2024-03-13 11:10:00 +0300"
git tag -a v1.1.0 -m "Listeleme seçeneği (-l) eklendi"
git tag -l
v1.0.0
v1.1.0
git tag -l "v1.0*"
v1.0.0
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:

git log --oneline --decorate -n 5
67c7ca6 (HEAD -> main, tag: v1.1.0) README'de -l seçeneğini belgele
a0e3bfe Tüm terimleri listeleyen -l seçeneğini ekle
11fe7e8 (tag: v1.0.0) Aramanın harf duyarsızlığını README'de belirt
c497738 Revert "Arama çıktısını yalnızca Türkçe sütuna indir"
d245350 Arama çıktısını yalnızca Türkçe sütuna indir

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

git for-each-ref refs/tags
9c57fc4649ef6bdcd4ee73dc697fe500468d50f6 tag	refs/tags/v1.0.0
7cbb6f6054352e79e94cf24d5079ae5af15482f1 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

git describe
v1.1.0

When a new commit is written after the tag, the output changes. A compound-term example is added to the README and committed:

cat >> README.md <<'END'

Bileşik terimleri tırnak içinde verin: `./ara.sh "bağlı liste"`.
END
export GIT_AUTHOR_DATE="2024-03-14 10:00:00 +0300"
export GIT_COMMITTER_DATE="2024-03-14 10:00:00 +0300"
git commit -a -m "Bileşik terim örneği ekle"
git describe
v1.1.0-1-g2a41365

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:

git log --oneline --decorate
2a41365 (HEAD -> main) Bileşik terim örneği ekle
67c7ca6 (tag: v1.1.0) README'de -l seçeneğini belgele
a0e3bfe Tüm terimleri listeleyen -l seçeneğini ekle
11fe7e8 (tag: v1.0.0) Aramanın harf duyarsızlığını README'de belirt
c497738 Revert "Arama çıktısını yalnızca Türkçe sütuna indir"
d245350 Arama çıktısını yalnızca Türkçe sütuna indir
377b1f8 README'ye kullanım bölümü ekle
0e74ba9 Öbek ve dizi terimlerini ekle
d3ab7b0 Yerel notları izlemeden çıkar
7e44d29 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.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ ↵ Esc navigate · open · close