Satu berkas terhapus bisa berarti dua keadaan yang sangat berbeda: rm tentang.md yang belum di-commit, atau penghapusan yang sudah telanjur menjadi commit riwayat. Keduanya dipulihkan dengan jalur berbeda — yang pertama cukup git restore, yang kedua menuntut pencarian ke belakang untuk menemukan commit terakhir yang masih memuat berkas itu. Chapter ini menuntaskan kedua jalur di repository yang sama, membedahkan perbedaan perilaku git checkout <hash> -- <berkas> versus git restore --source, lalu menutup dengan kasus ketiga yang paling licik: berkas yang lenyap melalui commit merge.

Kasus Pertama: Terhapus, Belum di-commit #

Penghapusan biasa hanyalah perubahan working tree — Git menyimpan salinan terakhirnya di index, jadi pemulihannya satu langkah. Pembahasan lengkap pola ini sudah pernah kita lewat di chapter Memulihkan File ; di sini kita jalankan versi ringkasnya. Repository latihan latihan-file berisi dua berkas ter-commit:

bash
mkdir latihan-file && cd latihan-file
git init -b main
git config user.name "Budi Santoso"
git config user.email "[email protected]"
printf '# Tentang\nkami toko contoh\n' > tentang.md
git add tentang.md
git commit -m "Buat halaman tentang"
printf 'beranda\n' > situs.md
git add situs.md
git commit -m "Buat halaman beranda"
rm tentang.md
git status

Output yang Dihasilkan:

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

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

Status menggolongkannya sebagai deleted — versi terakhir berkas masih tersimpan di index dan di commit. Satu perintah memulihkannya:

bash
git restore tentang.md
cat tentang.md

Output yang Dihasilkan:

text
# Tentang
kami toko contoh

Kasus Kedua: Terhapus dan Sudah di-commit #

Keadaan berubah total bila penghapusan sudah menjadi commit. Kini index dan HEAD sama-sama tak memuat berkas, sehingga git restore polos tidak lagi punya sumber. Langkah pertama adalah mencari commit terakhir yang masih memuat berkas itu — git log dengan path menampilkan seluruh commit yang pernah menyentuh berkas tersebut:

bash
git rm tentang.md
git commit -m "Hapus halaman tentang"
git log --oneline -- tentang.md

Output yang Dihasilkan:

text
f8a1674 Hapus halaman tentang
d8f2b08 Buat halaman tentang

Dua commit muncul: yang menghapus berkas dan yang membuatnya. Versi terakhir isi berkas hidup pada commit penciptaan d8f2b08 — atau commit mana pun di antara penciptaan dan penghapusan, karena isinya tak berubah sepanjang rentang itu. Hash di mesin pembaca akan berbeda; pola dua entrinya tetap.

Memulihkan dengan checkout hash – berkas #

Bentuk klasiknya menyalin berkas dari sebuah commit sekaligus ke index dan working tree:

bash
git checkout d8f2b08 -- tentang.md
git status --short

Output yang Dihasilkan:

text
A  tentang.md

Berkas kembali hadir dan langsung berstatus A — ter-staged untuk commit berikutnya. Keluaran A dan nama berkas dipisahkan TAB, sama seperti kolom status git status --short yang sudah kita kenal. Perilaku “otomatis staged” ini menghemat satu langkah git add, sekaligus menegaskan bahwa commit penghapusan belum dibatalkan: kita baru menambahkan kembali versi lama sebagai perubahan baru.

Memulihkan dengan restore –source #

Padanan modernnya, git restore --source, hanya menulis ke working tree dan membiarkan index tidak tersentuh. Supaya keduanya bisa dibandingkan dalam kondisi awal yang sama, commit penghapusan dikembalikan dulu:

bash
git reset --hard HEAD

Output yang Dihasilkan:

text
HEAD is now at f8a1674 Hapus halaman tentang

Sekarang restore --source mengambil berkas dari commit yang memuatnya — HEAD~1 juga berisi versi itu karena berkasnya baru terhapus di commit terbaru:

bash
git restore --source=HEAD~1 tentang.md
git status --short
cat tentang.md

Output yang Dihasilkan:

text
?? tentang.md

Output yang Dihasilkan:

text
# Tentang
kami toko contoh

Berkas kembali utuh, tetapi statusnya ?? — untracked — karena index tidak memuat tentang.md sama sekali. Berbeda dengan checkout, hasil restore --source perlu git add bila memang ingin di-commit ulang. Ringkasnya:

PerintahMenulis keStatus hasilLangkah lanjutan
git checkout <hash> -- <berkas>Index + working treeA (staged)Langsung git commit
git restore --source=<hash> <berkas>Working tree saja?? (untracked)git add dulu

Kasus Ketiga: Terhapus lewat commit merge #

Kasus terakhir ini jarang tetapi berbahaya: berkas dihapus di satu sisi merge, disunting di sisi lain, dan konfliknya diselesaikan dengan memihak penghapusan. Riwayat penghapusan lalu tersebar ke dua sisi. Repository latihan-file-merge mereproduksikannya dari nol:

bash
mkdir latihan-file-merge && cd latihan-file-merge
git init -b main
git config user.name "Budi Santoso"
git config user.email "[email protected]"
printf 'kebijakan: privasi\n' > kebijakan.md
git add kebijakan.md
git commit -m "Tulis kebijakan privasi"
git switch -c fitur-bersih
git rm kebijakan.md
git commit -m "Hapus kebijakan untuk revisi"
git switch main
printf 'kebijakan: privasi dan cookie\n' > kebijakan.md
git add kebijakan.md
git commit -m "Perluas kebijakan privasi"
git merge fitur-bersih

Output yang Dihasilkan:

text
CONFLICT (modify/delete): kebijakan.md deleted in fitur-bersih and modified in HEAD.  Version HEAD of kebijakan.md left in tree.
Automatic merge failed; fix conflicts and then commit the result.

Status singkatnya khas konflik modify/delete — berkas dihapus di satu sisi, dimodifikasi di sisi lain:

bash
git status --short

Output yang Dihasilkan:

text
UD kebijakan.md

Konflik modify/delete meletakkan versi main di working tree, sementara kode status UD menandakan berkas belum ter-merge (U) dan dihapus di sisi lawan (D). Kita memilih menyelesaikannya dengan git rm — kebijakan memang dihapus. Setelah commit penggabungan selesai, pencarian riwayat berkas dilakukan seperti biasa:

bash
git rm kebijakan.md
git commit -m "Gabungkan fitur-bersih"
git log --oneline -- kebijakan.md

Output yang Dihasilkan:

text
c5bd826 Hapus kebijakan untuk revisi
33544ba Tulis kebijakan privasi

Hanya dua commit yang tampil — dan keduanya dari jalur fitur-bersih! Commit “Perluas kebijakan privasi” di main serta commit merge yang benar-benar merekam penghapusan tidak muncul. Penyebabnya adalah history simplification: git log default menyederhanakan riwayat per berkas dan memilih satu jalur parent yang dianggap mewakili. Opsi --full-history mematikan penyederhanaan itu:

bash
git log --full-history --oneline -- kebijakan.md

Output yang Dihasilkan:

text
0753f07 Gabungkan fitur-bersih
3d9832d Perluas kebijakan privasi
c5bd826 Hapus kebijakan untuk revisi
33544ba Tulis kebijakan privasi

Keempat commit kini tampil semua. Versi terakhir berkas berada pada “Perluas kebijakan privasi” — parent pertama dari commit merge, yang bisa dijangkau tanpa mencari hash:

bash
git restore --source=HEAD^ kebijakan.md
cat kebijakan.md

Output yang Dihasilkan:

text
kebijakan: privasi dan cookie

Perintah acuan untuk seluruh kasus tadi ada di git-restore . Dari berkas kita naik ke satuan kerja terbesar yang bisa dibatalkan: commit terakhir yang ternyata keliru — tiga jalur pembatalannya dibedah di Membatalkan Commit Terakhir .