guide

Prioritas gitignore dan pola negasi (!)

Mengapa aturan terakhir yang menang, prioritas antara .gitignore, info/exclude dan konfigurasi global, serta mengapa ! tidak bisa menyertakan kembali file jika direktori induknya dikecualikan.

Saat aturan gitignore tidak berjalan sesuai harapan, penyebabnya hampir selalu urutan dan direktori induk. Tulisan ini merangkum aturan mana yang diikuti git ketika beberapa aturan berlaku.

Dalam satu file: aturan cocok terakhir yang menang

Di dalam satu .gitignore, git membaca dari atas ke bawah dan pola cocok terakhir yang menentukan hasil. Jika pola itu diawali !, file tidak diabaikan; jika tidak, file diabaikan.

*.log
!important.log

important.log cocok dengan kedua baris, tetapi baris terakhir adalah negasi sehingga file dilacak. Balik urutannya, maka hasilnya juga terbalik.

!important.log
*.log

Sekarang kecocokan terakhir important.log adalah *.log, jadi file diabaikan. Inilah sebabnya saat menggabungkan beberapa template di generator, urutan pemilihan menjadi urutan aturan. Misalnya, jika memakai *.jar dari template Java bersama !gradle-wrapper.jar dari template Gradle, Gradle harus berada di belakang.

Prioritas antarberkas

git membaca aturan dari empat tempat. Makin atas dalam daftar, makin tinggi prioritasnya.

  1. Pola dari baris perintah (beberapa perintah seperti git ls-files --exclude)
  2. .gitignore di direktori yang sama dengan jalur atau di direktori induknya — berkas yang lebih dalam menimpa berkas di atasnya
  3. $GIT_DIR/info/exclude (biasanya .git/info/exclude)
  4. Berkas yang ditunjuk pengaturan core.excludesFile (gitignore global)

Misalnya, jika .gitignore di root berisi *.tmp dan sub/.gitignore berisi !*.tmp, maka sub/y.tmp dilacak karena negasi di berkas yang lebih dalam. Sebaliknya, menulis !x di .git/info/exclude tidak berguna: x di .gitignore lebih diutamakan sehingga x tetap diabaikan.

$ git check-ignore -v x y.tmp sub/y.tmp
.gitignore:1:x	x
.gitignore:2:*.tmp	y.tmp
sub/.gitignore:1:!*.tmp	sub/y.tmp

Pengecualian terpenting: induk dikecualikan, tidak bisa disertakan kembali

Dokumentasi git menyatakan: file tidak dapat disertakan kembali jika salah satu direktori induknya dikecualikan. Demi kinerja, git sama sekali tidak masuk ke direktori yang dikecualikan, sehingga aturan ! untuk file di dalamnya tidak pernah dibaca.

logs/
!logs/keep.txt

Dengan aturan ini logs/keep.txt tetap diabaikan. Menjalankan git check-ignore -v menampilkan logs/ sebagai aturan penentu.

Solusinya adalah mengecualikan isi direktori, bukan direktorinya.

logs/*
!logs/keep.txt

logs/* tidak cocok dengan direktori logs itu sendiri, sehingga git masuk ke dalamnya dan menerapkan negasi pada keep.txt. /log/* + !/log/.keep di template Rails dan .vscode/* + !.vscode/settings.json di template VS Code memakai prinsip yang sama.

Untuk menyertakan kembali jalur yang lebih dalam, setiap direktori perantara juga harus disertakan kembali.

config/*
!config/app/
config/app/*
!config/app/defaults.yml

Pola daftar izin (allowlist)

Prinsip yang sama berlaku saat Anda ingin hanya mendaftar yang harus dilacak alih-alih yang harus diabaikan.

# abaikan semua di root
/*
# lalu sertakan kembali yang perlu
!/.gitignore
!/src/
!/README.md

/* cocok dengan setiap item di root tanpa mengecualikan root itu sendiri, sehingga !/src/ berfungsi. Dalam pola ini .gitignore sendiri juga terkena /*, jadi jangan lupa !/.gitignore. Memang, tanpa baris itu git status --ignored menampilkan .gitignore dalam daftar yang diabaikan.

Aturan ganda dan urutan

Jika aturan yang sama muncul berkali-kali, kemunculan sebelumnya tidak memengaruhi hasil, karena aturan yang sama di bawahnya selalu cocok belakangan. Namun menghapus duplikat yang di bawah bisa mengubah hasil bila ada negasi di antaranya.

*.log
!debug.log
*.log

Di sini, menghapus *.log terakhir membuat debug.log menjadi dilacak. Generator di situs ini mendeteksi kasus tersebut dan hanya menghapus duplikat di bawah bila tidak ada aturan negasi di antaranya, sedangkan sisanya dipertahankan.

Referensi

← SebelumnyaPanduan lengkap sintaks pola .gitignore