Skrip build baru selesai ditulis, lalu kita beri chmod +x supaya bisa dijalankan — isi berkas tidak berubah satu byte, tetapi git status tiba-tiba melaporkan modified dan git diff menampilkan baris aneh old mode 100644 / new mode 100755. Ini bukan bug. Dari seluruh permission Unix (rwx untuk pemilik, grup, dan lainnya), Git hanya menyimpan satu bit saja: executable atau tidak. Sistem berkas lain yang melaporkan mode seadanya — partisi FAT, network mount, tumpang tindih WSL dan Windows — memicu derau status yang sama tanpa perubahan apa pun. Chapter ini membedah mode berkas di index, diff yang murni berganti mode, peredam derau core.fileMode, serta cara menandai executable dari mesin yang tidak mengenal bit eksekusi: git update-index --chmod.

Git Hanya Menyimpan Bit Executable #

Permission Unix mengenal tiga bit per tiga kelompok (rwxr-xr-x dan sejenisnya). Git meringkasnya menjadi dua mode untuk berkas reguler — 100644 (tidak executable) dan 100755 (executable) — ditambah satu mode khusus 120000 untuk symbolic link yang dibahas pada chapter Symbolic Links . Tabelnya ringkas:

ModeArtiContoh berkas
100644berkas reguler tanpa bit eksekusidokumen, kode sumber, konfigurasi
100755berkas reguler executableskrip shell, program terpasang
120000symbolic link; isi blob berupa path targetpintasan antar-berkas

Mode ini tercatat di index, bukan di dalam blob. Repository latihan memperlihatkannya lewat git ls-files -s:

bash
mkdir latihan-mode && cd latihan-mode
git init -b main
git config user.name "Budi Santoso"
git config user.email "[email protected]"
printf '#!/bin/sh\necho "mem-bangun situs"\n' > bangun.sh
git add bangun.sh
git commit -m "Tambah skrip bangun"
git ls-files -s

Output yang Dihasilkan:

text
100644 044dc4d973062473f8cf70be76e8a23750a83548 0	bangun.sh

Format barisnya: <mode> <hash-blob> <stage> lalu TAB dan path. Skrip shell bangun.sh terekam 100644 — hash 044dc4d adalah hash isi berkas, dan 0 adalah nomor stage dari merge. Perhatikan satu hal penting untuk nanti: mode tidak ikut dihitung ke dalam hash blob.

chmod +x Memicu Diff Mode #

Skrip berjalan mulus di uji coba, jadi kita tandai executable:

bash
chmod +x bangun.sh
git status
git diff

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:   bangun.sh

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

Output yang Dihasilkan:

text
diff --git a/bangun.sh b/bangun.sh
old mode 100644
new mode 100755

Status melaporkan modified: bangun.sh, dan diff tidak memuat satu hunk isi pun — hanya dua baris old mode 100644 dan new mode 100755. Bukti terkuat bahwa isinya benar-benar tak tersentuh: setelah perubahan mode di-commit, hash blob tidak bergeser sedikit pun:

bash
git add bangun.sh
git commit -m "Tandai skrip bangun executable"
git ls-files -s

Output yang Dihasilkan:

text
100755 044dc4d973062473f8cf70be76e8a23750a83548 0	bangun.sh

Entri kini ber-mode 100755, tetapi hash blob-nya masih 044dc4d — persis hash sebelum chmod. Mode hidup di tree (lewat index), isi hidup di blob; keduanya independen. Karena itu perubahan mode adalah perubahan sungguhan yang layak di-commit: rekan yang men-clone menerima skrip yang langsung bisa dijalankan.

Diff Mode Bekerja di Dua Arah #

Arah sebaliknya berlaku sama. Kita cabut bit eksekusinya:

bash
chmod -x bangun.sh
git status
git diff

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:   bangun.sh

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

Output yang Dihasilkan:

text
diff --git a/bangun.sh b/bangun.sh
old mode 100755
new mode 100644

Baris mode kini terbalik: old mode 100755, new mode 100644. Bila diff ini di-commit dan di-push, klon rekan lain menerima skrip yang kehilangan hak eksekusinya — skrip bangun.sh akan menolak dijalankan sampai seseorang menaikkan kembali bitnya.

core.fileMode false: Meredam Derau Mode #

Skenario yang mengganggu: berada di filesystem yang melaporkan mode seadanya — partisi FAT yang tidak mengenal bit eksekusi Unix, network mount, atau direktori proyek yang diakses bersama Windows dan WSL. Status selalu menampilkan modified meski tak seorang pun menyentuh berkas, karena mode yang dilaporkan disk memang tak akan pernah cocok dengan index. Git menyediakan core.fileMode false untuk berhenti membandingkan bit eksekusi disk dengan index:

bash
git config core.fileMode false
git status

Output yang Dihasilkan:

text
On branch main
nothing to commit, working tree clean

Status kembali bersih — padahal bangun.sh di disk sedang tidak executable (kita baru saja chmod -x) sementara index masih memegang 100755. Konfigurasi ini tersimpan di konfigurasi lokal repository, tidak ikut di-push; memasangnya untuk semua repository cukup satu kali lewat git config --global core.fileMode false sebagaimana dibahas pada git config .

Menandai Executable tanpa Bit Disk: update-index –chmod #

Kasus klasiknya lintas sistem operasi: Budi menulis skrip di Windows — NTFS tidak mengenal bit eksekusi Unix, sehingga git add merekam 100644. Siti men-clone di Linux lalu gagal menjalankan skrip itu karena permission denied. Dari mesin apa pun, bahkan yang tidak punya bit eksekusi, git update-index --chmod menandai mode langsung pada index tanpa menyentuh disk.

Sandbox kita sedang berada dalam keadaan itu — core.fileMode false aktif dan disk tidak executable — jadi simulasi tinggal dilanjutkan. Perintah pertama menurunkan mode index, meniru commit yang dibuat dari mesin tanpa bit eksekusi:

bash
git update-index --chmod=-x bangun.sh
git status
git diff --cached

Output yang Dihasilkan:

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

Output yang Dihasilkan:

text
diff --git a/bangun.sh b/bangun.sh
old mode 100755
new mode 100644

Mode index turun ke 100644 sehingga status menampilkan modified di staging, dan git diff --cached menampilkan sepasang baris mode — persis pola diff chmod di awal chapter, kali ini murni hasil ubahan index. Kita bekukan sebagai “kecelakaan” lalu perbaiki dari mesin yang sama:

bash
git commit -m "Simulasikan commit tanpa bit eksekusi"
git update-index --chmod=+x bangun.sh
git status
git diff --cached

Output yang Dihasilkan:

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

Output yang Dihasilkan:

text
diff --git a/bangun.sh b/bangun.sh
old mode 100644
new mode 100755

Commit penutupnya dan hasil akhirnya:

bash
git commit -m "Tandai executable dari mesin tanpa bit eksekusi"
git ls-files -s

Output yang Dihasilkan:

text
100755 044dc4d973062473f8cf70be76e8a23750a83548 0	bangun.sh

Index kembali ke 100755 tanpa satu perintah chmod pun sejak core.fileMode false dipasang — dua commit terakhir memperlihatkan mode bergerak murni lewat index. Skrip yang ditulis Budi di Windows kini akan tiba executable di mesin Siti. Hash-hash commit di atas milik sandbox; nilai pembaca menyesuaikan mesin masing-masing.

Dua mode reguler itu hanya menyimpan satu bit, tetapi mode ketiga — 120000 — menutup cerita dengan perilaku yang sama sekali berbeda: isinya bukan isi berkas, melainkan path target berkas lain. Lanjut ke Symbolic Links .