Chapter sebelumnya menutup satu varian penolakan dengan resep fetch → gabungkan → push. Varian keduanya lebih dalam: penolakan non-fast-forward yang muncul ketika kita menulis ulang commit yang sudah dipublikasikan — lewat git commit --amend atau rebase — lalu mencoba push hasil tulisan barunya. Git menolak demi melindungi siapa pun yang sudah membangun di atas riwayat lama; melanggar perlindungan itu butuh --force, dan --force punya versi berpelindung: --force-with-lease. Chapter ini menjalankan ketiganya di satu panggung, lengkap dengan insiden hilangnya pekerjaan Siti di remote.

Ketika Riwayat Ditulis Ulang #

Lab memakai tiga babak persiapan. Pertama Budi mengirim catatan stok dari clone-nya:

bash
git push

Output yang Dihasilkan:

text
To /home/z/my-project/tutorial-extracted/work/sandbox/remote-repository/part-b/latihan-force/proyek-utama.git
   e2d6081..1382cfe  main -> main

Kedua, Siti meng-clone keadaan itu dan mengirim halaman kontak — remote maju lagi. Ketiga, Budi menariknya supaya salinannya jujur:

bash
git fetch origin

Output yang Dihasilkan:

text
From /home/z/my-project/tutorial-extracted/work/sandbox/remote-repository/part-b/latihan-force/proyek-utama
   1382cfe..0667253  main       -> origin/main

Baru kemudian kejadian intinya: Budi menelaah catatan stoknya, merasa isinya kurang, dan memperbaiki commit yang sudah terkirim itu — bukan dengan commit baru, melainkan menulis ulang yang lama:

bash
printf 'Catatan stok harian\n- kopi 12 kg\n' > catatan.md
git add catatan.md
git commit --amend -m "Lengkapi catatan stok"

Amend menciptakan commit baru yang menggantikan yang lama — mekanismenya dibedah di chapter Amend Commit . Commit lama masih tercatat di remote, dan salinan Budi memegang keduanya: riwayat barunya plus cermin origin/main. Push-nya ditolak:

bash
git push

Output yang Dihasilkan:

text
To /home/z/my-project/tutorial-extracted/work/sandbox/remote-repository/part-b/latihan-force/proyek-utama.git
 ! [rejected]        main -> main (non-fast-forward)
error: failed to push some refs to '/home/z/my-project/tutorial-extracted/work/sandbox/remote-repository/part-b/latihan-force/proyek-utama.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. If you want to integrate the remote changes,
hint: use 'git pull' before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

Alasannya berbeda dari fetch first di chapter Push Rejected : Budi sudah memegang commit terjauh remote — origin/main-nya segar — tetapi riwayat barunya tidak memuat commit lama yang masih duduk di remote. Push yang diloloskan akan membuang commit lama itu dan semua yang dibangun di atasnya. Penolakan non-fast-forward adalah kata “yakin?” dari Git.

Kekuatan yang Berbahaya: –force #

Jawaban paksa ada, dan namanya sejujur isi peringatannya:

bash
git push --force

Output yang Dihasilkan:

text
To /home/z/my-project/tutorial-extracted/work/sandbox/remote-repository/part-b/latihan-force/proyek-utama.git
 + 0667253...696fbc9 main -> main (forced update)

Penanda + dan keterangan (forced update) menandakan penunjuk main di remote digeser bukan maju, melainkan dipindahkan paksa ke riwayat tulisan baru Budi. Remote kini berbunyi: hanya Buat README toko dan Lengkapi catatan stok. Konsekuensinya terlihat dari mata Siti:

bash
git fetch

Output yang Dihasilkan (di clone siti):

text
From /home/z/my-project/tutorial-extracted/work/sandbox/remote-repository/part-b/latihan-force/proyek-utama
 + 0667253...696fbc9 main       -> origin/main  (forced update)
bash
git log --oneline origin/main

Output yang Dihasilkan (di clone siti):

text
696fbc9 Lengkapi catatan stok
e2d6081 Buat README toko
bash
git log --oneline main

Output yang Dihasilkan (di clone siti):

text
0667253 Tambah halaman kontak
1382cfe Tambah catatan stok
e2d6081 Buat README toko

Dua keluaran terakhir memperlihatkan tabrakan yang menyakitkan: cermin origin/main Siti sudah tidak lagi memuat Tambah halaman kontak, padahal commit itu masih hidup di main lokalnya. Pekerjaan Siti terkubur di remote — siapa pun yang meng-clone baru tidak akan pernah melihatnya. Cermin Siti yang bergerak mundur — baris + 0667253...696fbc9 (forced update) — adalah wajah dari pemaksaan itu. Siti masih bisa menyelamatkan komitnya karena memegang salinan lokal, dan objek Git yang terputus pun belum langsung terhapus sebelum pembersihan gc — jaring pengaman ini dibedah di chapter git reflog . Namun penyelamatan seperti itu hanya mungkin bagi yang kebetulan memegang salinannya.

–force-with-lease: Menolak Saat Kita Pergi Melamun #

Versi berpelindung menambahkan satu syarat kecil yang mengubah segalanya: push baru boleh menimpa bila kondisi remote masih sama seperti terakhir kali kita melihatnya — tepat pada posisi origin/* di salinan kita. Bila remote sudah bergerak sejak itu, push gagal. Skenarionya kita lanjutkan: Siti menerima kenyataan, membarui clone-nya, lalu mengirim halaman promo baru:

bash
git reset --hard origin/main
git push

Output yang Dihasilkan (perintah kedua, di clone siti):

text
To /home/z/my-project/tutorial-extracted/work/sandbox/remote-repository/part-b/latihan-force/proyek-utama.git
   696fbc9..6b1a8b0  main -> main

git reset --hard memindahkan branch aktif ke titik lain dan menimpa working tree — alatnya dibedah di chapter Reset: Soft, Mixed, Hard . Remote maju lagi. Budi, yang salinannya tertinggal satu langkah, kini merapikan catatan stok sekali lagi dengan amend — lalu mencoba lease:

bash
git push --force-with-lease

Output yang Dihasilkan:

text
To /home/z/my-project/tutorial-extracted/work/sandbox/remote-repository/part-b/latihan-force/proyek-utama.git
 ! [rejected]        main -> main (stale info)
error: failed to push some refs to '/home/z/my-project/tutorial-extracted/work/sandbox/remote-repository/part-b/latihan-force/proyek-utama.git'

Penanda (stale info) memberitahu persis penyakitnya: informasi lease Budi basi — catatan terakhirnya menyebut remote di posisi lama, sedangkan remote kini memegang commit promo Siti yang belum pernah dilihat Budi. Tanpa --force-with-lease, --force akan mengubur commit promo itu persis seperti insiden sebelumnya.

Lease Menolak: Saatnya Melihat dulu #

Penolakan lease bukan halangan, melainkan pintu inspeksi. Budi menarik keadaan sebenarnya:

bash
git fetch origin

Output yang Dihasilkan:

text
From /home/z/my-project/tutorial-extracted/work/sandbox/remote-repository/part-b/latihan-force/proyek-utama
   696fbc9..6b1a8b0  main       -> origin/main

Sekarang Budi melihat promo Siti, dan pilihannya sadar: perbaikan kecil itu tidak layak menimpa apa pun. Ia membuang tulisan ulangnya dan mengulang perbaikan sebagai commit baru di atas keadaan terkini:

bash
git reset --hard origin/main

Output yang Dihasilkan:

text
HEAD is now at 6b1a8b0 Tambah halaman promo
bash
printf 'Catatan stok harian\n- kopi 12 kg\n- gula 5 kg\n' > catatan.md
git add catatan.md
git commit -m "Rapikan catatan stok"
git push

Output yang Dihasilkan (perintah terakhir):

text
To /home/z/my-project/tutorial-extracted/work/sandbox/remote-repository/part-b/latihan-force/proyek-utama.git
   6b1a8b0..4dd7b3e  main -> main

Rentang maju biasa tanpa pemaksaan — kedua pekerjaan hidup berdampingan.

Lease Lolos saat Remote Bersih #

Skenario pamungkas: keadaan di mana menulis ulang memang wajar. Budi baru saja push, lalu sadar pesan commit terakhirnya kurang tepat dan menyempurnakannya dengan amend. Push biasa ditolak non-fast-forward — tetapi remote tidak bergerak sejak push itu, posisi yang tercatat salinannya masih akurat:

bash
git push --force-with-lease

Output yang Dihasilkan:

text
To /home/z/my-project/tutorial-extracted/work/sandbox/remote-repository/part-b/latihan-force/proyek-utama.git
 + 4dd7b3e...bca1ce6 main -> main (forced update)

Lease lolos karena yang ditimpa hanya versi lama milik Budi sendiri — persis skenario “memperbaiki commit sendiri yang baru terkirim” atau “mempublikasikan ulang branch pribadi setelah rebase”. Menjadikannya kebiasaan bisa lewat konfigurasi:

bash
git config push.forceWithLease true

Dengan konfigurasi itu, git push --force polos pun diperlakukan sebagai --force-with-lease. Rincian keduanya terdokumentasi di dokumentasi git push .

SituasiAlat yang tepatAlasan
Branch pribadi, ingin menulis ulang--force-with-leasehanya kita yang membangun di atasnya
Publish ulang setelah rebase--force-with-lease setelah fetchpastikan lihat dulu kondisi terkini
Membenahi commit sendiri yang baru terkirim--force-with-leaseyang ditimpa versi lama kita
Branch bersama seperti mainjangan force — gabungkanpekerjaan rekan ikut terkubur

Kini kedua penolakan push dan seluruh jalan keluarnya sudah kita pegang: fetch first diselesaikan penggabungan, non-fast-forward ditangani lease dengan mata terbuka. Alat pertukarannya lengkap — yang belum hanyalah kebiasaan memakainya dalam irama kerja harian. Chapter berikutnya merangkainya menjadi satu rutinitas penuh, dari pull pagi hingga push penutup.