Chapter sebelumnya memperkenalkan .gitignore dengan lima pola praktis. Kebutuhan proyek nyata lebih rinci: menyamarkan direktori di kedalaman mana pun tetapi mengecualikan satu berkas, menjangkarkan pola ke root, atau memakai aturan pribadi yang tidak dibagikan tim. Semua itu dicapai lewat variasi tanda bintang, posisi garis miring, dan tanda seru. Chapter ini membedah tiap bentuk pola pada satu repository latihan: tiap tahap menambah satu pola, lalu mengamati git status --short dan git check-ignore -v sebagai bukti hidup.
Menyiapkan Pohon Latihan #
Pohon latihan sengaja menguji tiap pola — berkas log di dua kedalaman, direktori hasil, berkas biasa yang kebetulan bernama node_modules, konfigurasi lokal di root dan di alat/, serta direktori logs di dua lokasi:
mkdir latihan-pola && cd latihan-pola
git init -b main
git config user.name "Budi Santoso"
git config user.email "[email protected]"
printf '# Proyek Toko Berkah\n' > README.md
git add README.md
git commit -m "Buat beranda proyek"
printf 'catatan pengerjaan desain\n' > catatan-pengerjaan.log
printf 'catatan penting yang ingin dilacak\n' > penting.log
printf 'berkas biasa bernama node_modules\n' > node_modules
mkdir -p hasil logs/app paket/logs alat aset sub
printf 'laporan hasil uji\n' > hasil/laporan.html
printf 'log utama aplikasi\n' > logs/utama.txt
printf 'log debug\n' > logs/debug
printf 'log debug modul\n' > logs/app/debug
printf 'isi log paket\n' > paket/logs/isi.txt
printf 'panduan memakai alat\n' > alat/panduan.txt
printf 'KUNCI_ALAT=contoh\n' > alat/config.local
printf 'KUNCI=contoh\n' > config.local
printf 'catatan pengerjaan submodul\n' > sub/catatan-pengerjaan.log
git add alat/panduan.txt
git commit -m "Tambah panduan alat"
git status --shortOutput yang Dihasilkan:
?? alat/config.local
?? catatan-pengerjaan.log
?? config.local
?? hasil/
?? logs/
?? node_modules
?? paket/
?? penting.log
?? sub/Format --short merangkum satu path per baris dengan kode ?? dari chapter Status File
. Direktori yang seluruh isinya untracked diringkat menjadi logs/; hanya alat/config.local tampil per berkas karena alat/ memuat berkas tracked.
Bintang: *.log Menyamarkan di Kedalaman Mana Pun #
Glob * berarti “karakter apa pun sebanyak apa pun”. Karena *.log tidak memuat garis miring, ia dicocokkan terhadap nama berkas pada setiap kedalaman:
printf '*.log\n' > .gitignore
git status --shortOutput yang Dihasilkan:
?? .gitignore
?? alat/config.local
?? config.local
?? hasil/
?? logs/
?? node_modules
?? paket/catatan-pengerjaan.log dan sub/catatan-pengerjaan.log lenyap sekaligus — penting.log pun ikut; dia kita kembalikan nanti lewat negasi:
git check-ignore -v catatan-pengerjaan.log sub/catatan-pengerjaan.logOutput yang Dihasilkan:
.gitignore:1:*.log catatan-pengerjaan.log
.gitignore:1:*.log sub/catatan-pengerjaan.logGaris Miring di Akhir: Hanya Direktori #
Pola berakhiran / hanya mencocokkan direktori. Dua pola berikut memperlihatkan batasnya — di sandbox ini hasil hadir sebagai direktori sementara node_modules berupa berkas biasa:
printf 'node_modules/\nhasil/\n' >> .gitignore
git status --shortOutput yang Dihasilkan:
?? .gitignore
?? alat/config.local
?? config.local
?? logs/
?? node_modules
?? paket/Direktori hasil/ lenyap beserta isinya, tetapi baris ?? node_modules tetap tampil: pola berakhiran / tidak menangkap berkas biasa yang kebetulan bernama sama.
Garis Miring di Awal: Jangkar ke Root #
Menambahkan / di depan menjangkarkan pola ke root repository:
printf '/config.local\n' >> .gitignore
git status --shortOutput yang Dihasilkan:
?? .gitignore
?? alat/config.local
?? logs/
?? node_modules
?? paket/config.local di root lenyap; alat/config.local tetap tampil. Diagnosisnya tegas:
git check-ignore -v config.local; git check-ignore -v alat/config.local; echo "kode status: $?"Output yang Dihasilkan:
.gitignore:4:/config.local config.local
kode status: 1Perintah pertama menunjuk pola /config.local pada baris 4; perintah kedua sunyi dengan kode status 1 — tidak ada pola yang menangkap path di dalam alat/.
Dua Bintang: ** untuk Kedalaman Berapa Pun #
**/ berarti “nol atau lebih direktori”, sehingga **/logs menangkap direktori bernama logs di root maupun lebih dalam:
printf '**/logs\n' >> .gitignore
git status --shortOutput yang Dihasilkan:
?? .gitignore
?? alat/config.local
?? node_moduleslogs/ dan paket/ hilang bersamaan — paket/ lenyap karena satu-satunya isinya tersamarkan:
git check-ignore -v logs/utama.txt paket/logs/isi.txtOutput yang Dihasilkan:
.gitignore:5:**/logs logs/utama.txt
.gitignore:5:**/logs paket/logs/isi.txtPosisi ** di tengah pola pun sah: logs/**/debug menyasar path bernama debug di bawah logs berapa pun lapisnya. Kita ganti baris **/logs agar hanya berkas debug yang disembunyikan:
cat > .gitignore <<'EOF'
*.log
node_modules/
hasil/
/config.local
logs/**/debug
EOF
git status --shortOutput yang Dihasilkan:
?? .gitignore
?? alat/config.local
?? logs/
?? node_modules
?? paket/Kedua direktori kembali tampil karena hanya path bernama debug yang kini tersamarkan — dan sesuai semantik “nol atau lebih direktori”, logs/debug satu tingkat di bawah root logs juga ikut:
git check-ignore -v logs/app/debug logs/debugOutput yang Dihasilkan:
.gitignore:5:logs/**/debug logs/app/debug
.gitignore:5:logs/**/debug logs/debugNegasi dengan Tanda Seru #
Baris diawali ! membatalkan pola sebelumnya. Pola *.log menyamarkan penting.log; satu baris negasi mengembalikannya:
printf '!penting.log\n' >> .gitignore
git status --shortOutput yang Dihasilkan:
?? .gitignore
?? alat/config.local
?? logs/
?? node_modules
?? paket/
?? penting.logpenting.log muncul kembali sebagai untracked — siap di-git add seperti biasa:
git check-ignore -v penting.logOutput yang Dihasilkan:
.gitignore:6:!penting.log penting.logBila beberapa pola cocok dengan satu path, yang menang adalah baris paling akhir. Konvensi praktisnya: taruh negasi tepat setelah pola yang dibatalkan agar maksudnya terbaca berpasangan.
Jebakan Negasi: Direktori yang Di-exclude Tak Bisa Dibuka #
Ada satu batas keras: bila sebuah direktori tersamarkan, Git tidak lagi menuruni isinya, sehingga negasi pada anaknya tidak pernah sampai. Kita coba kembalikan hasil/laporan.html dari balik pola hasil/:
printf '!hasil/laporan.html\n' >> .gitignore
git check-ignore -v hasil/laporan.htmlOutput yang Dihasilkan:
.gitignore:3:hasil/ hasil/laporan.htmllaporan.html tidak muncul di status, dan check-ignore -v menunjuk pelakunya: pola hasil/ pada baris 3 menangkap path anaknya. Solusinya: samarkan isi direktori dengan hasil/*, bukan direktorinya — kini Git tetap menuruni hasil/ dan negasi pada anaknya bisa menang:
cat > .gitignore <<'EOF'
*.log
node_modules/
hasil/*
/config.local
logs/**/debug
!penting.log
!hasil/laporan.html
EOF
git status --shortOutput yang Dihasilkan:
?? .gitignore
?? alat/config.local
?? hasil/
?? logs/
?? node_modules
?? paket/
?? penting.logBaris ?? hasil/ muncul kembali — direktori itu kini berisi berkas yang tidak diabaikan:
git check-ignore -v hasil/laporan.htmlOutput yang Dihasilkan:
.gitignore:7:!hasil/laporan.html hasil/laporan.htmlHierarki: .gitignore Subdirektori Menimpa #
.gitignore boleh hadir di banyak direktori, dan pola yang lebih dekat dengan path menang:
printf '!catatan-pengerjaan.log\n' > sub/.gitignore
git status --shortOutput yang Dihasilkan:
?? .gitignore
?? alat/config.local
?? hasil/
?? logs/
?? node_modules
?? paket/
?? penting.log
?? sub/Direktori sub/ muncul kembali. Perbandingan dua kedalaman memperlihatkan pembagian kerjanya:
git check-ignore -v catatan-pengerjaan.log sub/catatan-pengerjaan.logOutput yang Dihasilkan:
.gitignore:1:*.log catatan-pengerjaan.log
sub/.gitignore:1:!catatan-pengerjaan.log sub/catatan-pengerjaan.logBerkas di root ditangkap pola *.log milik .gitignore root; berkas di sub/ ditangkap negasi milik sub/.gitignore yang lebih dekat — dan sub/.gitignore sendiri bisa ikut di-commit bila aturannya berlaku untuk seluruh tim.
Pola Pribadi: core.excludesFile #
Kadang kita butuh aturan khusus mesin sendiri — editor Budi menaburkan draf-pribadi.txt, aturan yang tidak relevan bagi Siti. Penampungnya berkas di luar repository, didaftarkan lewat core.excludesFile:
printf 'draf-pribadi.txt\n' > ../eksklusi-pribadi.txt
printf 'draf pribadi Budi\n' > draf-pribadi.txt
git status --short
git config core.excludesFile ../eksklusi-pribadi.txt
git status --shortOutput yang Dihasilkan (sebelum dan sesudah konfigurasi):
?? .gitignore
?? alat/config.local
?? draf-pribadi.txt
?? hasil/
?? logs/
?? node_modules
?? paket/
?? penting.log
?? sub/?? .gitignore
?? alat/config.local
?? hasil/
?? logs/
?? node_modules
?? paket/
?? penting.log
?? sub/git config --get core.excludesFileOutput yang Dihasilkan:
../eksklusi-pribadi.txtPada praktiknya konfigurasi ini dipasang sekali untuk semua repository lewat git config --global core.excludesFile ~/.gitignore_global; tanpa konfigurasi, Git sudah membaca ~/.config/git/ignore. Prioritasnya paling rendah — pola .gitignore repository selalu menang, sehingga aturan tim tak bisa dibatalkan selera pribadi.
Menyusun Pola: Manual Dahulu, Template Sebagai Rujukan #
Dua rujukan populer: situs gitignore.io yang menyusun gabungan pola sesuai kombinasi alat, dan repositori github/gitignore
berisi template resmi untuk ratusan bahasa dan framework. Sikap sehat: susun .gitignore manual dari derau yang benar-benar muncul — tiap baris punya cerita — lalu rujuk template hanya memastikan tak ada pola penting yang terlewat. Pola yang tidak dipahami sulit didiagnosis saat kelak ada berkas penting yang “kok tidak muncul”.
| Pola | Arti |
|---|---|
*.log | berkas berakhiran .log di kedalaman mana pun |
node_modules/ | direktori bernama node_modules beserta seluruh isinya |
/config.local | hanya config.local di root repository |
**/logs | direktori logs di kedalaman berapa pun, termasuk root |
logs/**/debug | path bernama debug di bawah logs berapa pun lapisnya, termasuk logs/debug |
!penting.log | negasi — mengembalikan berkas yang ditangkap pola sebelumnya |
hasil/* + !hasil/laporan.html | samarkan isi hasil/ lalu kembalikan satu berkasnya |
Baterai pola ini lengkap untuk berkas untracked. Pertanyaan tersisa datang dari arah sebaliknya: bagaimana bila pola baru ditulis setelah berkas terlanjur di-commit dan dilacak? Git tidak akan berhenti melacaknya dengan sendirinya — langkah eksplisitnya dibahas pada chapter Mengabaikan File yang Terlanjur Dilacak .