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:
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 statusOutput yang Dihasilkan:
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:
git restore tentang.md
cat tentang.mdOutput yang Dihasilkan:
# Tentang
kami toko contohKasus 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:
git rm tentang.md
git commit -m "Hapus halaman tentang"
git log --oneline -- tentang.mdOutput yang Dihasilkan:
f8a1674 Hapus halaman tentang
d8f2b08 Buat halaman tentangDua 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:
git checkout d8f2b08 -- tentang.md
git status --shortOutput yang Dihasilkan:
A tentang.mdBerkas 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:
git reset --hard HEADOutput yang Dihasilkan:
HEAD is now at f8a1674 Hapus halaman tentangSekarang restore --source mengambil berkas dari commit yang memuatnya — HEAD~1 juga berisi versi itu karena berkasnya baru terhapus di commit terbaru:
git restore --source=HEAD~1 tentang.md
git status --short
cat tentang.mdOutput yang Dihasilkan:
?? tentang.mdOutput yang Dihasilkan:
# Tentang
kami toko contohBerkas 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:
| Perintah | Menulis ke | Status hasil | Langkah lanjutan |
|---|---|---|---|
git checkout <hash> -- <berkas> | Index + working tree | A (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:
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-bersihOutput yang Dihasilkan:
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:
git status --shortOutput yang Dihasilkan:
UD kebijakan.mdKonflik 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:
git rm kebijakan.md
git commit -m "Gabungkan fitur-bersih"
git log --oneline -- kebijakan.mdOutput yang Dihasilkan:
c5bd826 Hapus kebijakan untuk revisi
33544ba Tulis kebijakan privasiHanya 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:
git log --full-history --oneline -- kebijakan.mdOutput yang Dihasilkan:
0753f07 Gabungkan fitur-bersih
3d9832d Perluas kebijakan privasi
c5bd826 Hapus kebijakan untuk revisi
33544ba Tulis kebijakan privasiKeempat commit kini tampil semua. Versi terakhir berkas berada pada “Perluas kebijakan privasi” — parent pertama dari commit merge, yang bisa dijangkau tanpa mencari hash:
git restore --source=HEAD^ kebijakan.md
cat kebijakan.mdOutput yang Dihasilkan:
kebijakan: privasi dan cookiePerintah 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 .