Situs toko kita sudah punya riwayat rapi: beranda, katalog, lalu beberapa perbaikan. Suatu hari sebuah pertanyaan muncul: commit mana persisnya yang berisi “katalog pertama kali jadi”? Mengingat hash acak seperti 16ac351 tidak realistis untuk manusia, sementara branch terus melaju meninggalkan commit lama. Git menyediakan mekanisme khusus untuk masalah ini: tag — nama tetap yang menempel pada satu commit dan tidak pernah berpindah. Chapter ini membedah bagaimana tag bekerja di balik layar, mengapa tag berbeda fundamental dari branch, dan kapan tag menjadi alat yang tepat.

Tag adalah Penunjuk Tetap ke Satu Commit #

Di level paling rendah, tag hanyalah berkas kecil berisi satu hash. Kita bangun repository latihan, tandai satu commit, lalu intip isi .git secara langsung:

bash
mkdir latihan-konsep && cd latihan-konsep
git init -b main
git config user.name "Budi Santoso"
git config user.email "[email protected]"
printf '# Toko Berkah\nberanda\n' > situs.md
git add situs.md
git commit -m "Buat beranda toko"
printf 'daftar produk unggulan\n' > katalog.md
git add katalog.md
git commit -m "Tambah halaman katalog"
git tag v1.0.0

Perintah git tag v1.0.0 berjalan sunyi tanpa keluaran — tag dibuat pada commit yang sedang menjadi HEAD. Isinya bisa dibuktikan langsung dengan membaca berkas ref-nya:

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

Output yang Dihasilkan:

text
16ac351ebc9ad6c2d9bc206a3aedeced413d6d5e
bash
cat .git/refs/heads/main

Output yang Dihasilkan:

text
16ac351ebc9ad6c2d9bc206a3aedeced413d6d5e

Kedua berkas berisi hash yang sama: 16ac351 — commit “Tambah halaman katalog”. Hash itu dihitung dari isi commit sehingga nilainya berbeda di mesin pembaca; yang penting justru kesamaan kedua nilai itu. Tag lightweight seperti ini benar-benar cuma penunjuk: tidak ada objek tambahan, tidak ada pesan, hanya satu baris hash.

Branch Bergerak, Tag Diam #

Perbedaan mendasah dengan branch baru terasa ketika commit baru datang. Kita tambahkan dua commit lagi:

bash
printf '# Toko Berkah\nberanda\nmenu: beranda | katalog\n' > situs.md
git add situs.md
git commit -m "Perbaiki menu navigasi"
printf '[email protected]\n' > kontak.md
git add kontak.md
git commit -m "Tambah halaman kontak"

Kini kita baca ulang kedua berkas ref yang tadi:

bash
cat .git/refs/heads/main

Output yang Dihasilkan:

text
23052964548660e688d68cedca8b9c98c17cc2b8
bash
cat .git/refs/tags/v1.0.0

Output yang Dihasilkan:

text
16ac351ebc9ad6c2d9bc206a3aedeced413d6d5e

Berkas main berpindah ke 2305296 (commit terbaru), sedangkan v1.0.0 masih menempel pada 16ac351 — persis seperti saat dibuat. Perilaku ini bukan kebetulan: Git menggeser refs/heads/main setiap kali commit baru dibuat di branch itu, tetapi tidak pernah menyentuh refs/tags/* atas inisiatifnya sendiri. Hasilnya terlihat dari kedua sisi:

bash
git rev-parse main

Output yang Dihasilkan:

text
23052964548660e688d68cedca8b9c98c17cc2b8
bash
git rev-parse v1.0.0

Output yang Dihasilkan:

text
16ac351ebc9ad6c2d9bc206a3aedeced413d6d5e

Skema berikut merangkum hubungan itu pada repository kita:

Pada diagram, tag dan branch sama-sama menunjuk ke commit, tetapi hanya main yang panahnya bergeser ke kanan setiap ada commit baru. git log --oneline --decorate memperlihatkan keadaan akhirnya:

bash
git log --oneline --decorate --all

Output yang Dihasilkan:

text
2305296 (HEAD -> main) Tambah halaman kontak
5b6986a Perbaiki menu navigasi
16ac351 (tag: v1.0.0) Tambah halaman katalog
089d8d9 Buat beranda toko

Baris 16ac351 (tag: v1.0.0) tetap diam di tengah riwayat sementara HEAD -> main terus berada di ujung. Mekanisme penunjuk ber-hash ini adalah kelanjutan dari pembahasan Commit Hash dan References ; tag hanyalah salah satu penghuni namespace ref.

Namespace refs/tags/ #

Git menyimpan seluruh penunjuk di bawah .git/refs/ yang terbagi per jenis:

bash
ls .git/refs

Output yang Dihasilkan:

text
heads
tags
bash
ls .git/refs/tags

Output yang Dihasilkan:

text
v1.0.0

Direktori heads/ menampung branch lokal dan tags/ menampung tag — keduanya hidup berdampingan tanpa saling menimpa nama, jadi tag v1.0.0 dan branch v1.0.0 bisa eksis bersamaan. Konsekuensi praktisnya: tag tidak pernah muncul di daftar branch.

bash
git branch

Output yang Dihasilkan:

text
* main

Meskipun v1.0.0 sudah dibuat, git branch hanya melaporkan main. Pemisahan namespace ini pula yang membuat perintah git push origin v1.0.0 tahu harus mengirim ref tag, bukan branch — detail yang dibedah tuntas di chapter Berbagi Tag ke Remote .

Tag untuk Titik Penting #

Kegunaan utama tag adalah memberi nama pada commit penting. Dua kasus paling umum:

  • Rilis: v1.0.0, v2.3.1 — commit yang dikirim ke pengguna. Tag menjadi jawaban presisi untuk “versi 2.3.1 dibangun dari commit mana?”
  • Checkpoint: eksperimen-berhasil, demo-klien-2026 — titik yang ingin dirujuk cepat tanpa harus mengingat hash, misalnya batas sebelum refactor besar.

Karena tidak bergerak, tag aman dirujuk jauh setelah dibuat: git checkout v1.0.0, git diff v1.0.0..main, atau git log v1.0.0.. akan membaca titik yang sama berapa kali pun perintah itu diulang. Inilah fondasi seluruh alur rilis di section ini — dari Semantic Versioning sampai Release Workflow .

Tag Bukan Branch Rilis #

Praktik branch release/1.0 yang dibahas di Strategi Branching sering disamakan dengan tag, padahal keduanya berbeda filosofi. Branch rilis adalah garis pengembangan yang tetap menerima commit perbaikan — branch itu hidup dan bergerak. Tag adalah potret satu titik pada satu waktu — sekali dicap, anggap selesai. Skenario singkat perbandingannya:

AspekBranch release/1.0Tag v1.0.0
Menunjuk kecommit terbaru branch (bergerak)satu commit tetap
Menerima commitya, misalnya perbaikan hotfixtidak pernah
Kegunaanmerawat versi yang beredarmenandai versi yang dirilis
Pasangan idealdipotret dengan tag tiap rilis—

Dalam praktik keduanya bersahabat: branch release/1.0 merawat perbaikan, lalu setiap titik rilis dari branch itu dipotret sebagai tag v1.0.1, v1.0.2, dan seterusnya.

Satu hal belum kita bahas: git tag v1.0.0 barusan membuat jenis tag paling sederhana. Ada jenis kedua yang menyimpan pesan, penanda waktu, dan nama pembuat sebagai objek penuh di database Git. Perbedaan keduanya menentukan pilihan pada tiap rilis nyata — itulah dua chapter berikutnya, dimulai dari Lightweight Tag .