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 describegives 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.