Kebiasaan buruk yang hampir semua orang alami: .env berisi kredensial di-commit lebih dulu, barulah kemudian .gitignore ditulis — urutannya terbalik. Pasalnya, .gitignore hanya menyamarkan berkas untracked; sekali sebuah berkas masuk riwayat, Git terus mengawasinya apa pun kata pola. Chapter ini membuktikan perilaku itu dengan suntingan nyata, lalu menempuh prosedur resminya: git rm --cached mengeluarkan berkas dari index tanpa menyentuh disk, sehingga pola .gitignore akhirnya bekerja. Konsekuensi terakhirnya tidak kalah penting — berkas yang berhenti dilacak tetap tergantung di riwayat lama.

Bukti: .gitignore Tidak Menghentikan Pelacakan #

Repository latihan sengaja memuat kecelakaan itu sejak commit pertama — situs.md dan .env dilacak bersamaan, lalu .gitignore menyusul di commit kedua:

bash
mkdir latihan-terlanjur && cd latihan-terlanjur
git init -b main
git config user.name "Budi Santoso"
git config user.email "[email protected]"
printf '# Situs Toko Berkah\nberanda toko\n' > situs.md
printf 'KUNCI_API=contoh-rahasia\n' > .env
git add situs.md .env
git commit -m "Buat beranda dan konfigurasi"
printf '.env\n' > .gitignore
git add .gitignore
git commit -m "Abaikan berkas .env"

Sekarang kita sunting .env seolah nilai kuncinya berganti:

bash
printf 'KUNCI_API=contoh-rahasia-baru\n' > .env
git status

Output yang Dihasilkan:

text
On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   .env

no changes added to commit (use "git add" and/or "git commit -a")

.env muncul sebagai modified meski pola .env tercantum di .gitignore sejak commit sebelumnya. Dengan git diff, suntingannya bahkan terbaca utuh:

bash
git diff

Output yang Dihasilkan:

text
diff --git a/.env b/.env
index c3266a8..7b26e64 100644
--- a/.env
+++ b/.env
@@ -1 +1 @@
-KUNCI_API=contoh-rahasia
+KUNCI_API=contoh-rahasia-baru

Mekanismenya sudah kita kenal dari chapter Untracked dan Tracked Files : status membandingkan index dengan disk, dan index masih memuat entri .env. Pengecekan .gitignore hanya berlaku pada berkas yang belum dikenal Git — pelacakannya harus kita hentikan secara eksplisit.

Urutan kejadian di sandbox ini meniru kecelakaan dunia nyata: aturan .gitignore ditulis setelah berkas terlanjur masuk riwayat, sehingga aturan itu kehilangan kesempatan bekerja sejak awal.

git rm –cached: Keluar dari Index, Tetap di Disk #

Perintah git rm --cached menghapus entri dari index — snapshot berikutnya tidak lagi memuat .env — tanpa menghapus berkas fisiknya:

bash
git rm --cached .env

Output yang Dihasilkan:

text
rm '.env'

Baris rm '.env' menandakan penghapusan dicatat di index, bukan di disk. Status dan isi folder memastikan keduanya sekaligus:

bash
git status
ls .env

Output yang Dihasilkan:

text
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	deleted:    .env
text
.env

Bagian Changes to be committed memuat deleted: .env — itulah perubahan yang akan dicatat commit berikutnya. Berkasnya sendiri masih ada di disk (ls berhasil), dan kini berstatus untracked yang tersamarkan pola, sehingga tidak muncul di daftar untracked. Kita selesaikan transaksinya:

bash
git commit -m "Hentikan pelacakan .env"

Output yang Dihasilkan:

text
[main 4edb8c1] Hentikan pelacakan .env
 1 file changed, 1 deletion(-)
 delete mode 100644 .env

Hash 4edb8c1 milik sandbox — nilai pembaca menyesuaikan. Status pun kini sunyi sepenuhnya:

bash
git status

Output yang Dihasilkan:

text
On branch main
nothing to commit, working tree clean

Sejak commit ini, pola .env benar-benar memegang kendali: suntingan .env berikutnya tidak akan pernah muncul di status, dan git add . pun tak akan menangkapnya.

Berkasnya Tetap Tergantung di Riwayat Lama #

Berhenti dilacak bukan berarti terhapus dari sejarah. Commit lama masih menyimpan salinan .env apa adanya:

bash
git log --oneline -- .env
git show HEAD~1:.env

Output yang Dihasilkan:

text
4edb8c1 Hentikan pelacakan .env
a9d8e11 Buat beranda dan konfigurasi
text
KUNCI_API=contoh-rahasia

Riwayat path .env memuat dua commit, dan git show HEAD~1:.env mengeluarkan isi berkas pada commit sebelum “Hentikan pelacakan .env” — kuncinya masih terbaca. Siapa pun yang men-clone repository bisa menelusuri jalur yang sama; menulis ulang .gitignore tidak menutup pintu itu.

Direktori Terlanjur: git rm -r –cached #

Masalah yang sama sering datang dalam ukuran lebih besar — seluruh direktori terlanjur ter-commit. Skenario kita melanjutkan repo yang sama: direktori hasil/ sempat dilacak, lalu pola hasil/ menyusul:

bash
mkdir hasil
printf 'laporan uji v1\n' > hasil/laporan.md
git add hasil
git commit -m "Tambah laporan hasil uji"
printf 'hasil/\n' >> .gitignore
git add .gitignore
git commit -m "Abaikan direktori hasil"

Opsi -r membuat --cached bekerja rekursif pada seluruh isi direktori:

bash
git rm -r --cached hasil/

Output yang Dihasilkan:

text
rm 'hasil/laporan.md'
bash
git status
ls hasil/

Output yang Dihasilkan:

text
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	deleted:    hasil/laporan.md
text
laporan.md

Satu berkas di dalamnya tercatat sebagai deleted di staging, sementara ls membuktikan isi hasil/ masih utuh di disk — perilaku yang sama dengan kasus .env, hanya dikerjakan berlapis. Commit penutupnya identik:

bash
git commit -m "Hentikan pelacakan direktori hasil"

Output yang Dihasilkan:

text
[main 322e6c3] Hentikan pelacakan direktori hasil
 1 file changed, 1 deletion(-)
 delete mode 100644 hasil/laporan.md
bash
git status

Output yang Dihasilkan:

text
On branch main
nothing to commit, working tree clean

Pola lengkapnya kini tertutup: pencegahan lewat .gitignore , penghentian pelacakan lewat git rm --cached. Sisanya — git rm itu sendiri beserta jaring pengaman dan kaitannya dengan rm biasa — menjadi fokus chapter berikutnya. Lanjut ke git rm .