Chapter sebelumnya memetakan keluarga refs: branch, tag, remote — semuanya berkas berisi hash. Satu penunjuk sengaja kita tunda: HEAD, penunjuk paling istimewa karena menjadi jawaban Git atas pertanyaan “kita sedang berada di mana”. Kebanyakan waktu HEAD bekerja diam-diam lewat nama branch, tetapi sesekali Git menampilkan status yang membuat semua panik: HEAD detached at 1a2b3c4. Chapter ini membuka kedua wajah HEAD langsung dari berkasnya, membuktikan kapan HEAD menjadi penunjuk tak langsung dan kapan menjadi hash telanjang, lalu menyaksikan sendiri mengapa commit yang dibuat saat detached berada dalam posisi terancam — dan bagaimana penunjuk @{-1} membawa kita kembali.
HEAD Attached: Penunjuk Tak Langsung #
Kita bangun repository latihan tiga commit dengan satu tag di commit pertama:
mkdir latihan-head && cd latihan-head
git init -b main
git config user.name "Budi Santoso"
git config user.email "[email protected]"
printf 'Keranjang:\n- kopi\n' > keranjang.md
git add keranjang.md
git commit -m "Buat keranjang belanja"
git tag -a v1.0.0 -m "Rilis pertama"
printf 'Keranjang:\n- kopi\n- gula\n' > keranjang.md
git add keranjang.md
git commit -m "Tambah gula"
printf 'Keranjang:\n- kopi\n- gula\n- teh\n' > keranjang.md
git add keranjang.md
git commit -m "Tambah teh"Dalam keadaan normal — Git menyebutnya attached — isi berkas HEAD bukan hash, melainkan nama ref:
cat .git/HEADOutput yang Dihasilkan:
ref: refs/heads/mainBentuk ref: refs/heads/main berarti HEAD menunjuk ke branch, dan branch itulah yang menunjuk commit — rantai dua lompatan. Perintah plumbing git symbolic-ref membaca penunjuk tak langsung ini secara eksplisit:
git symbolic-ref HEADOutput yang Dihasilkan:
refs/heads/maingit symbolic-ref --short HEADOutput yang Dihasilkan:
mainInilah mekanisme di balik nama branch pada prompt terminal dan git status: keduanya membaca HEAD, mengikuti penunjuknya ke refs/heads/main, lalu melaporkan main. Ketika kita membuat commit, alurnya pun jelas: commit baru ditulis, berkas refs/heads/main digeser ke commit baru, dan HEAD tidak berubah sama sekali. Peta lengkap posisi kita:
Wajah Kedua: HEAD Detached #
Berpindah ke sebuah commit — bukan branch — memutus rantai dua lompatan itu. Perintah git switch --detach sengaja diminta saat kita memotret rilis lama:
git switch --detach v1.0.0Output yang Dihasilkan:
HEAD is now at 4fecd2f Buat keranjang belanjaMari kita baca isi berkas HEAD sekarang:
cat .git/HEADOutput yang Dihasilkan:
4fecd2f22a947d3c45053e9ffb5a93222888ee7aHash telanjang — HEAD tidak lagi menunjuk branch mana pun; keadaan ini disebut detached HEAD. Status memaparkannya dengan jelas:
git statusOutput yang Dihasilkan:
HEAD detached at v1.0.0
nothing to commit, working tree cleanGit 2.47 bahkan mengingat nama yang kita pakai saat melepaskan diri: detached at v1.0.0. Konsekuensi mekanisnya: commit baru tidak lagi punya branch yang otomatis digeser, karena tidak ada ref yang dipegang HEAD. git symbolic-ref menolak keras:
git symbolic-ref HEADOutput yang Dihasilkan:
fatal: ref HEAD is not a symbolic refPerintah itu keluar dengan kode galat 128 — HEAD memang bukan symbolic ref saat detached. Kedua wajah ini punya deteksi resminya: git symbolic-ref berhasil berarti attached, gagal berarti detached. Kondisi detached sendiri bukan kesalahan — justru berguna untuk memotret keadaan rilis lama, membandingkan dua commit, atau eksperimen singkat yang tidak ingin meninggalkan branch; git switch
memperkenalkannya sebagai mode kerja sah.
Commit Saat Detached: Pekerjaan di Udara Tipis #
Yang berbahaya adalah membuat commit saat detached. Kita coba — suntingan kecil untuk rilis yang sedang kita tinjau:
printf 'catatan: sedang uji coba di v1.0.0\n' > catatan.txt
git add catatan.txt
git commit -m "Catatan uji coba rilis"Commit berhasil dibuat; git log --oneline -3 memperlihatkannya di puncak:
git log --oneline -3Output yang Dihasilkan:
3f5fb6d Catatan uji coba rilis
4fecd2f Buat keranjang belanjaStatus kini bergeser dari detached at menjadi detached from:
git statusOutput yang Dihasilkan:
HEAD detached from v1.0.0
nothing to commit, working tree cleanBedanya tajam: at berarti kita masih berdiri di titik pelepasan, from berarti kita sudah melangkah menjauh darinya. Sekarang saatnya kembali ke main — dan di sinilah Git memberi peringatan yang terkenal itu:
git switch mainOutput yang Dihasilkan:
Warning: you are leaving 1 commit behind, not connected to
any of your branches:
3f5fb6d Catatan uji coba rilis
If you want to keep it by creating a new branch, this may be a good time
to do so with:
git branch <new-branch-name> 3f5fb6d
Switched to branch 'main'Commit 3f5fb6d tertinggal tanpa branch yang menunjuknya — hanya HEAD (yang kini sudah pindah) dan reflog yang masih mengenalnya. Git sendiri menyarankan obatnya: tempelkan branch baru pada hash itu dengan git branch <nama> 3f5fb6d. Prosedur penyelamatan lengkapnya — termasuk mengapa objek tak ditunjuk tetap hidup dan kapan git gc benar-benar menghapusnya — sudah dibahas tuntas pada Menangani Detached HEAD
; cukup diingat di sini: commit detached yang ditinggalkan tidak langsung hilang, tapi posisinya memang rawan.
Kembali dengan switch - dan Notasi @{-1} #
Bagaimana pulang ke posisi sebelumnya? Notasi @{-1} berarti “posisi checkout sebelumnya” (@{-2} dua langkah sebelumnya, dst.), dan git switch - adalah singkatannya. Kita coba:
git switch -Output yang Dihasilkan:
fatal: a branch is expected, got commit '3f5fb6de1ccbfb0d20aafa6e77018ddd772d4c7c'
hint: If you want to detach HEAD at the commit, try again with the --detach option.git switch - menolak — @{-1} kita memang berupa commit lepas, bukan branch, dan switch hanya mau menerima branch tanpa --detach. Petunjuknya sendiri jelas: ulangi dengan --detach. Tapi @{-1} tetap menyelesaikan alamatnya dengan benar:
git rev-parse @{-1}Output yang Dihasilkan:
3f5fb6de1ccbfb0d20aafa6e77018ddd772d4c7cCommit yang tadi tertinggal — persis. Untuk melihat switch - bekerja mulus, pindah dulu antar branch:
git switch -c ujiOutput yang Dihasilkan:
Switched to a new branch 'uji'git switch -Output yang Dihasilkan:
Switched to branch 'main'Dari uji kembali ke main dalam satu ketik — karena @{-1} kini berupa branch. Riwayat perpindahan lengkapnya terbaca dari reflog:
git reflogOutput yang Dihasilkan:
d13ed34 HEAD@{0}: checkout: moving from uji to main
d13ed34 HEAD@{1}: checkout: moving from main to uji
d13ed34 HEAD@{2}: checkout: moving from 3f5fb6de1ccbfb0d20aafa6e77018ddd772d4c7c to main
3f5fb6d HEAD@{3}: commit: Catatan uji coba rilis
4fecd2f HEAD@{4}: checkout: moving from main to v1.0.0
d13ed34 HEAD@{5}: commit: Tambah teh
bf3dcfd HEAD@{6}: commit: Tambah gula
4fecd2f HEAD@{7}: commit (initial): Buat keranjang belanjaDelapan baris memuat seluruh perjalanan kita: tiga commit awal, pelepasan ke v1.0.0, commit saat detached, kepulangan ke main, lalu bolak-balik uji. Notasi @{-1}, @{-2} membaca daftar ini dari atas; reflog dan notasi HEAD@{N} dibedah penuh di Git Reflog
. Detail perilaku symbolic-ref tersedia di dokumentasinya: git symbolic-ref
.
Tiga pilar kini terkuak: objek (isi riwayat), refs (penunjuk bernama), dan HEAD (posisi kita). Tersisa satu mekanisme yang setiap hari kita pakai tanpa sadari namanya: index — berkas misterius .git/index yang menjadi jantung staging area. Perintah git add menulisnya, git status membacanya, dan konflik merge membelahnya menjadi tiga panggung. Chapter penutup bagian ini, Index dan Cache
, membedahnya sampai ke format berkasnya.